diff --git a/.circleci/config.yml b/.circleci/config.yml
index 794ce64ad1a..3dff8a52ca6 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,24 +2,15 @@ version: 2
jobs:
go-version-latest:
docker:
- - image: cimg/go:1.22-node
+ - image: cimg/go:1.22-node
resource_class: large
steps:
- - checkout
- - run: make
- - run: make alltest
- go-version-last:
- docker:
- - image: cimg/go:1.21-node
- resource_class: large
- steps:
- - checkout
- - run: make
- - run: make alltest
+ - checkout
+ - run: make
+ - run: make alltest
workflows:
version: 2
build_and_test:
jobs:
- - go-version-latest
- - go-version-last
+ - go-version-latest
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index 18f69d52438..b3e2cb43c39 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -23,7 +23,7 @@ jobs:
uses: golangci/golangci-lint-action@v4
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
- version: v1.56
+ version: v1.57
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
diff --git a/.golangci.yml b/.golangci.yml
index cd92e43c89f..e2f8a240991 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,5 +1,5 @@
service:
- golangci-lint-version: 1.56.x # use the fixed version to not introduce new linters unexpectedly
+ golangci-lint-version: 1.57.x # use the fixed version to not introduce new linters unexpectedly
run:
concurrency: 4
@@ -8,23 +8,6 @@ run:
build-tags:
- integ
- integfuzz
- # which dirs to skip: they won't be analyzed;
- # can use regexp here: generated.*, regexp is applied on full path;
- # default value is empty list, but next dirs are always skipped independently
- # from this option's value:
- # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
- skip-dirs:
- - genfiles$
- - vendor$
- - bin$
-
- # which files to skip: they will be analyzed, but issues from them
- # won't be reported. Default value is empty list, but there is
- # no need to include all autogenerated files, we confidently recognize
- # autogenerated files. If it's not please let us know.
- skip-files:
- - ".*\\.pb\\.go"
- - ".*\\.gen\\.go"
linters:
disable-all: true
@@ -136,6 +119,14 @@ issues:
- unparam
text: "is always false"
+ exclude-dirs:
+ - genfiles$
+ - vendor$
+ - bin$
+ exclude-files:
+ - ".*\\.pb\\.go"
+ - ".*\\.gen\\.go"
+
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
diff --git a/Makefile.cross-compiles b/Makefile.cross-compiles
index 3c123df8af6..8887cad4bee 100644
--- a/Makefile.cross-compiles
+++ b/Makefile.cross-compiles
@@ -1,8 +1,8 @@
-export PATH := $(GOPATH)/bin:$(PATH)
+export PATH := $(PATH):`go env GOPATH`/bin
export GO111MODULE=on
LDFLAGS := -s -w
-os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64
+os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 android:arm64
all: build
@@ -15,8 +15,8 @@ app:
gomips=$(shell echo "$(n)" | cut -d : -f 3);\
target_suffix=$${os}_$${arch};\
echo "Build $${os}-$${arch}...";\
- env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frpc_$${target_suffix} ./cmd/frpc;\
- env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frps_$${target_suffix} ./cmd/frps;\
+ env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -tags frpc -o ./release/frpc_$${target_suffix} ./cmd/frpc;\
+ env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -tags frps -o ./release/frps_$${target_suffix} ./cmd/frps;\
echo "Build $${os}-$${arch} done";\
)
@mv ./release/frpc_windows_amd64 ./release/frpc_windows_amd64.exe
diff --git a/README.md b/README.md
index cf6b7cf5495..69cce9ddc48 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
[![Build Status](https://circleci.com/gh/fatedier/frp.svg?style=shield)](https://circleci.com/gh/fatedier/frp)
[![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases)
+[![Go Report Card](https://goreportcard.com/badge/github.com/fatedier/frp)](https://goreportcard.com/report/github.com/fatedier/frp)
+[![GitHub Releases Stats](https://img.shields.io/github/downloads/fatedier/frp/total.svg?logo=github)](https://somsubhra.github.io/github-release-stats/?username=fatedier&repository=frp)
[README](README.md) | [中文文档](README_zh.md)
@@ -11,6 +13,10 @@
+  
+
+
+
@@ -72,6 +78,7 @@ frp also offers a P2P connect mode.
* [URL Routing](#url-routing)
* [TCP Port Multiplexing](#tcp-port-multiplexing)
* [Connecting to frps via PROXY](#connecting-to-frps-via-proxy)
+ * [Port range mapping](#port-range-mapping)
* [Client Plugins](#client-plugins)
* [Server Manage Plugins](#server-manage-plugins)
* [SSH Tunnel Gateway](#ssh-tunnel-gateway)
@@ -1152,6 +1159,24 @@ serverPort = 7000
transport.proxyURL = "http://user:pwd@192.168.1.128:8080"
```
+### Port range mapping
+
+*Added in v0.56.0*
+
+We can use the range syntax of Go template combined with the built-in `parseNumberRangePair` function to achieve port range mapping.
+
+The following example, when run, will create 8 proxies named `test-6000, test-6001 ... test-6007`, each mapping the remote port to the local port.
+
+```
+{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
+[[proxies]]
+name = "tcp-{{ $v.First }}"
+type = "tcp"
+localPort = {{ $v.First }}
+remotePort = {{ $v.Second }}
+{{- end }}
+```
+
### Client Plugins
frpc only forwards requests to local TCP or UDP ports by default.
diff --git a/README_zh.md b/README_zh.md
index d78875c40d4..2c902f9f049 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -2,6 +2,8 @@
[![Build Status](https://circleci.com/gh/fatedier/frp.svg?style=shield)](https://circleci.com/gh/fatedier/frp)
[![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases)
+[![Go Report Card](https://goreportcard.com/badge/github.com/fatedier/frp)](https://goreportcard.com/report/github.com/fatedier/frp)
+[![GitHub Releases Stats](https://img.shields.io/github/downloads/fatedier/frp/total.svg?logo=github)](https://somsubhra.github.io/github-release-stats/?username=fatedier&repository=frp)
[README](README.md) | [中文文档](README_zh.md)
@@ -13,6 +15,10 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP
+  
+
+
+
diff --git a/Release.md b/Release.md
index c0d90bbc868..163ca3728a1 100644
--- a/Release.md
+++ b/Release.md
@@ -1,3 +1,7 @@
### Features
-* Proxy supports configuring annotations, which will be displayed in the frps dashboard.
+* `https2http` and `https2https` plugin now supports `X-Forwared-For` header.
+
+### Fixes
+
+* `X-Forwared-For` header is now correctly set in the request to the backend server for proxy type http.
diff --git a/assets/frps/static/index-ky4re_ta.js b/assets/frps/static/index-Q42Pu2_S.js
similarity index 99%
rename from assets/frps/static/index-ky4re_ta.js
rename to assets/frps/static/index-Q42Pu2_S.js
index cf7b8c3b515..45293e05cfd 100644
--- a/assets/frps/static/index-ky4re_ta.js
+++ b/assets/frps/static/index-Q42Pu2_S.js
@@ -81,4 +81,4 @@ PERFORMANCE OF THIS SOFTWARE.
`:"
",g=c.join(m);this._showOrMove(s,function(){this._updateContentNotChangedOnAxis(r,u)?this._updatePosition(s,d,o[0],o[1],this._tooltipContent,u):this._showTooltipContent(s,g,u,Math.random()+"",o[0],o[1],d,null,h)})},t.prototype._showSeriesItemTooltip=function(r,n,i){var a=this._ecModel,o=dt(n),s=o.seriesIndex,l=a.getSeriesByIndex(s),u=o.dataModel||l,f=o.dataIndex,c=o.dataType,h=u.getData(c),d=this._renderMode,v=r.positionDefault,p=bl([h.getItemModel(f),u,l&&(l.coordinateSystem||{}).model],this._tooltipModel,v?{position:v}:null),m=p.get("trigger");if(!(m!=null&&m!=="item")){var g=u.getDataParams(f,c),y=new up;g.marker=y.makeTooltipMarker("item",ku(g.color),d);var _=FS(u.formatTooltip(f,!1,c)),b=p.get("order"),x=p.get("valueFormatter"),w=_.frag,S=w?WS(x?ue({valueFormatter:x},w):w,y,d,b,a.get("useUTC"),p.get("textStyle")):_.text,C="item_"+u.name+"_"+f;this._showOrMove(p,function(){this._showTooltipContent(p,S,g,C,r.offsetX,r.offsetY,r.position,r.target,y)}),i({type:"showTip",dataIndexInside:f,dataIndex:h.getRawIndex(f),seriesIndex:s,from:this.uid})}},t.prototype._showComponentItemTooltip=function(r,n,i){var a=dt(n),o=a.tooltipConfig,s=o.option||{};if(Ee(s)){var l=s;s={content:l,formatter:l}}var u=[s],f=this._ecModel.getComponent(a.componentMainType,a.componentIndex);f&&u.push(f),u.push({formatter:s.content});var c=r.positionDefault,h=bl(u,this._tooltipModel,c?{position:c}:null),d=h.get("content"),v=Math.random()+"",p=new up;this._showOrMove(h,function(){var m=tt(h.get("formatterParams")||{});this._showTooltipContent(h,d,m,v,r.offsetX,r.offsetY,r.position,n,p)}),i({type:"showTip",from:this.uid})},t.prototype._showTooltipContent=function(r,n,i,a,o,s,l,u,f){if(this._ticket="",!(!r.get("showContent")||!r.get("show"))){var c=this._tooltipContent;c.setEnterable(r.get("enterable"));var h=r.get("formatter");l=l||r.get("position");var d=n,v=this._getNearestPoint([o,s],i,r.get("trigger"),r.get("borderColor")),p=v.color;if(h)if(Ee(h)){var m=r.ecModel.get("useUTC"),g=ye(i)?i[0]:i,y=g&&g.axisType&&g.axisType.indexOf("time")>=0;d=h,y&&(d=Ph(g.axisValue,d,m)),d=pE(d,i,!0)}else if(Ye(h)){var _=xt(function(b,x){b===this._ticket&&(c.setContent(x,f,r,p,l),this._updatePosition(r,l,o,s,c,i,u))},this);this._ticket=a,d=h(i,a,_)}else d=h;c.setContent(d,f,r,p,l),c.show(r,p),this._updatePosition(r,l,o,s,c,i,u)}},t.prototype._getNearestPoint=function(r,n,i,a){if(i==="axis"||ye(n))return{color:a||(this._renderMode==="html"?"#fff":"none")};if(!ye(n))return{color:a||n.color||n.borderColor}},t.prototype._updatePosition=function(r,n,i,a,o,s,l){var u=this._api.getWidth(),f=this._api.getHeight();n=n||r.get("position");var c=o.getSize(),h=r.get("align"),d=r.get("verticalAlign"),v=l&&l.getBoundingRect().clone();if(l&&v.applyTransform(l.transform),Ye(n)&&(n=n([i,a],s,o.el,v,{viewSize:[u,f],contentSize:c.slice()})),ye(n))i=yt(n[0],u),a=yt(n[1],f);else if(Re(n)){var p=n;p.width=c[0],p.height=c[1];var m=Ls(p,{width:u,height:f});i=m.x,a=m.y,h=null,d=null}else if(Ee(n)&&l){var g=$ee(n,v,c,r.get("borderWidth"));i=g[0],a=g[1]}else{var g=Bee(i,a,o,u,f,h?null:20,d?null:20);i=g[0],a=g[1]}if(h&&(i-=CC(h)?c[0]/2:h==="right"?c[0]:0),d&&(a-=CC(d)?c[1]/2:d==="bottom"?c[1]:0),_D(r)){var g=Fee(i,a,o,u,f);i=g[0],a=g[1]}o.moveTo(i,a)},t.prototype._updateContentNotChangedOnAxis=function(r,n){var i=this._lastDataByCoordSys,a=this._cbParamsList,o=!!i&&i.length===r.length;return o&&R(i,function(s,l){var u=s.dataByAxis||[],f=r[l]||{},c=f.dataByAxis||[];o=o&&u.length===c.length,o&&R(u,function(h,d){var v=c[d]||{},p=h.seriesDataIndices||[],m=v.seriesDataIndices||[];o=o&&h.value===v.value&&h.axisType===v.axisType&&h.axisId===v.axisId&&p.length===m.length,o&&R(p,function(g,y){var _=m[y];o=o&&g.seriesIndex===_.seriesIndex&&g.dataIndex===_.dataIndex}),a&&R(h.seriesDataIndices,function(g){var y=g.seriesIndex,_=n[y],b=a[y];_&&b&&b.data!==_.data&&(o=!1)})})}),this._lastDataByCoordSys=r,this._cbParamsList=n,!!o},t.prototype._hide=function(r){this._lastDataByCoordSys=null,r({type:"hideTip",from:this.uid})},t.prototype.dispose=function(r,n){He.node||!n.getDom()||(wm(this,"_updatePosition"),this._tooltipContent.dispose(),Fm("itemTooltip",n))},t.type="tooltip",t}(Mi);function bl(e,t,r){var n=t.ecModel,i;r?(i=new sr(r,n,n),i=new sr(t.option,i,n)):i=t;for(var a=e.length-1;a>=0;a--){var o=e[a];o&&(o instanceof sr&&(o=o.get("tooltip",!0)),Ee(o)&&(o={formatter:o}),o&&(i=new sr(o,i,n)))}return i}function xC(e,t){return e.dispatchAction||xt(t.dispatchAction,t)}function Bee(e,t,r,n,i,a,o){var s=r.getSize(),l=s[0],u=s[1];return a!=null&&(e+l+a+2>n?e-=l+a:e+=a),o!=null&&(t+u+o>i?t-=u+o:t+=o),[e,t]}function Fee(e,t,r,n,i){var a=r.getSize(),o=a[0],s=a[1];return e=Math.min(e+o,n)-o,t=Math.min(t+s,i)-s,e=Math.max(e,0),t=Math.max(t,0),[e,t]}function $ee(e,t,r,n){var i=r[0],a=r[1],o=Math.ceil(Math.SQRT2*n)+8,s=0,l=0,u=t.width,f=t.height;switch(e){case"inside":s=t.x+u/2-i/2,l=t.y+f/2-a/2;break;case"top":s=t.x+u/2-i/2,l=t.y-a-o;break;case"bottom":s=t.x+u/2-i/2,l=t.y+f+o;break;case"left":s=t.x-i-o,l=t.y+f/2-a/2;break;case"right":s=t.x+u+o,l=t.y+f/2-a/2}return[s,l]}function CC(e){return e==="center"||e==="middle"}function Hee(e,t,r){var n=v0(e).queryOptionMap,i=n.keys()[0];if(!(!i||i==="series")){var a=lf(t,i,n.get(i),{useDefault:!1,enableAll:!1,enableNone:!1}),o=a.models[0];if(o){var s=r.getViewOfComponentModel(o),l;if(s.group.traverse(function(u){var f=dt(u).tooltipConfig;if(f&&f.name===e.name)return l=u,!0}),l)return{componentMainType:i,componentIndex:o.componentIndex,el:l}}}}const zee=Nee;function Vee(e){va(yD),e.registerComponentModel(See),e.registerComponentView(zee),e.registerAction({type:"showTip",event:"showTip",update:"tooltip:manuallyShowTip"},Er),e.registerAction({type:"hideTip",event:"hideTip",update:"tooltip:manuallyHideTip"},Er)}var Wee=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.layoutMode={type:"box",ignoreSize:!0},r}return t.type="title",t.defaultOption={z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bold",color:"#464646"},subtextStyle:{fontSize:12,color:"#6E7079"}},t}(Mt),Gee=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.render=function(r,n,i){if(this.group.removeAll(),!!r.get("show")){var a=this.group,o=r.getModel("textStyle"),s=r.getModel("subtextStyle"),l=r.get("textAlign"),u=Ze(r.get("textBaseline"),r.get("textVerticalAlign")),f=new mr({style:ha(o,{text:r.get("text"),fill:o.getTextColor()},{disableBox:!0}),z2:10}),c=f.getBoundingRect(),h=r.get("subtext"),d=new mr({style:ha(s,{text:h,fill:s.getTextColor(),y:c.height+r.get("itemGap"),verticalAlign:"top"},{disableBox:!0}),z2:10}),v=r.get("link"),p=r.get("sublink"),m=r.get("triggerEvent",!0);f.silent=!v&&!m,d.silent=!p&&!m,v&&f.on("click",function(){bS(v,"_"+r.get("target"))}),p&&d.on("click",function(){bS(p,"_"+r.get("subtarget"))}),dt(f).eventData=dt(d).eventData=m?{componentType:"title",componentIndex:r.componentIndex}:null,a.add(f),h&&a.add(d);var g=a.getBoundingRect(),y=r.getBoxLayoutParams();y.width=g.width,y.height=g.height;var _=Ls(y,{width:i.getWidth(),height:i.getHeight()},r.get("padding"));l||(l=r.get("left")||r.get("right"),l==="middle"&&(l="center"),l==="right"?_.x+=_.width:l==="center"&&(_.x+=_.width/2)),u||(u=r.get("top")||r.get("bottom"),u==="center"&&(u="middle"),u==="bottom"?_.y+=_.height:u==="middle"&&(_.y+=_.height/2),u=u||"top"),a.x=_.x,a.y=_.y,a.markRedraw();var b={align:l,verticalAlign:u};f.setStyle(b),d.setStyle(b),g=a.getBoundingRect();var x=_.margin,w=r.getItemStyle(["color","opacity"]);w.fill=r.get("backgroundColor");var S=new Gt({shape:{x:g.x-x[3],y:g.y-x[0],width:g.width+x[1]+x[3],height:g.height+x[0]+x[2],r:r.get("borderRadius")},style:w,subPixelOptimize:!0,silent:!0});a.add(S)}},t.type="title",t}(Mi);function Uee(e){e.registerComponentModel(Wee),e.registerComponentView(Gee)}var Yee=function(e,t){if(t==="all")return{type:"all",title:e.getLocaleModel().get(["legend","selector","all"])};if(t==="inverse")return{type:"inverse",title:e.getLocaleModel().get(["legend","selector","inverse"])}},jee=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.layoutMode={type:"box",ignoreSize:!0},r}return t.prototype.init=function(r,n,i){this.mergeDefaultAndTheme(r,i),r.selected=r.selected||{},this._updateSelector(r)},t.prototype.mergeOption=function(r,n){e.prototype.mergeOption.call(this,r,n),this._updateSelector(r)},t.prototype._updateSelector=function(r){var n=r.selector,i=this.ecModel;n===!0&&(n=r.selector=["all","inverse"]),ye(n)&&R(n,function(a,o){Ee(a)&&(a={type:a}),n[o]=st(a,Yee(i,a.type))})},t.prototype.optionUpdated=function(){this._updateData(this.ecModel);var r=this._data;if(r[0]&&this.get("selectedMode")==="single"){for(var n=!1,i=0;i=0},t.prototype.getOrient=function(){return this.get("orient")==="vertical"?{index:1,name:"vertical"}:{index:0,name:"horizontal"}},t.type="legend.plain",t.dependencies=["series"],t.defaultOption={z:4,show:!0,orient:"horizontal",left:"center",top:0,align:"auto",backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderRadius:0,borderWidth:0,padding:5,itemGap:10,itemWidth:25,itemHeight:14,symbolRotate:"inherit",symbolKeepAspect:!0,inactiveColor:"#ccc",inactiveBorderColor:"#ccc",inactiveBorderWidth:"auto",itemStyle:{color:"inherit",opacity:"inherit",borderColor:"inherit",borderWidth:"auto",borderCap:"inherit",borderJoin:"inherit",borderDashOffset:"inherit",borderMiterLimit:"inherit"},lineStyle:{width:"auto",color:"inherit",inactiveColor:"#ccc",inactiveWidth:2,opacity:"inherit",type:"inherit",cap:"inherit",join:"inherit",dashOffset:"inherit",miterLimit:"inherit"},textStyle:{color:"#333"},selectedMode:!0,selector:!1,selectorLabel:{show:!0,borderRadius:10,padding:[3,5,3,5],fontSize:12,fontFamily:"sans-serif",color:"#666",borderWidth:1,borderColor:"#666"},emphasis:{selectorLabel:{show:!0,color:"#eee",backgroundColor:"#666"}},selectorPosition:"auto",selectorItemGap:7,selectorButtonGap:10,tooltip:{show:!1}},t}(Mt);const $m=jee;var jo=Ot,Hm=R,wc=Ur,qee=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.newlineDisabled=!1,r}return t.prototype.init=function(){this.group.add(this._contentGroup=new wc),this.group.add(this._selectorGroup=new wc),this._isFirstRender=!0},t.prototype.getContentGroup=function(){return this._contentGroup},t.prototype.getSelectorGroup=function(){return this._selectorGroup},t.prototype.render=function(r,n,i){var a=this._isFirstRender;if(this._isFirstRender=!1,this.resetInner(),!!r.get("show",!0)){var o=r.get("align"),s=r.get("orient");(!o||o==="auto")&&(o=r.get("left")==="right"&&s==="vertical"?"right":"left");var l=r.get("selector",!0),u=r.get("selectorPosition",!0);l&&(!u||u==="auto")&&(u=s==="horizontal"?"end":"start"),this.renderInner(o,r,n,i,l,s,u);var f=r.getBoxLayoutParams(),c={width:i.getWidth(),height:i.getHeight()},h=r.get("padding"),d=Ls(f,c,h),v=this.layoutInner(r,o,d,a,l,u),p=Ls(ht({width:v.width,height:v.height},f),c,h);this.group.x=p.x-v.x,this.group.y=p.y-v.y,this.group.markRedraw(),this.group.add(this._backgroundEl=bee(v,r))}},t.prototype.resetInner=function(){this.getContentGroup().removeAll(),this._backgroundEl&&this.group.remove(this._backgroundEl),this.getSelectorGroup().removeAll()},t.prototype.renderInner=function(r,n,i,a,o,s,l){var u=this.getContentGroup(),f=je(),c=n.get("selectedMode"),h=[];i.eachRawSeries(function(d){!d.get("legendHoverLink")&&h.push(d.id)}),Hm(n.getData(),function(d,v){var p=d.get("name");if(!this.newlineDisabled&&(p===""||p===`
`)){var m=new wc;m.newline=!0,u.add(m);return}var g=i.getSeriesByName(p)[0];if(!f.get(p))if(g){var y=g.getData(),_=y.getVisual("legendLineStyle")||{},b=y.getVisual("legendIcon"),x=y.getVisual("style"),w=this._createItem(g,p,v,d,n,r,_,x,b,c,a);w.on("click",jo(TC,p,null,a,h)).on("mouseover",jo(zm,g.name,null,a,h)).on("mouseout",jo(Vm,g.name,null,a,h)),f.set(p,!0)}else i.eachRawSeries(function(S){if(!f.get(p)&&S.legendVisualProvider){var C=S.legendVisualProvider;if(!C.containName(p))return;var M=C.indexOfName(p),A=C.getItemVisual(M,"style"),P=C.getItemVisual(M,"legendIcon"),E=uo(A.fill);E&&E[3]===0&&(E[3]=.2,A=ue(ue({},A),{fill:s0(E,"rgba")}));var L=this._createItem(S,p,v,d,n,r,{},A,P,c,a);L.on("click",jo(TC,null,p,a,h)).on("mouseover",jo(zm,null,p,a,h)).on("mouseout",jo(Vm,null,p,a,h)),f.set(p,!0)}},this)},this),o&&this._createSelector(o,n,a,s,l)},t.prototype._createSelector=function(r,n,i,a,o){var s=this.getSelectorGroup();Hm(r,function(u){var f=u.type,c=new mr({style:{x:0,y:0,align:"center",verticalAlign:"middle"},onclick:function(){i.dispatchAction({type:f==="all"?"legendAllSelect":"legendInverseSelect"})}});s.add(c);var h=n.getModel("selectorLabel"),d=n.getModel(["emphasis","selectorLabel"]);E0(c,{normal:h,emphasis:d},{defaultText:u.title}),um(c)})},t.prototype._createItem=function(r,n,i,a,o,s,l,u,f,c,h){var d=r.visualDrawType,v=o.get("itemWidth"),p=o.get("itemHeight"),m=o.isSelected(n),g=a.get("symbolRotate"),y=a.get("symbolKeepAspect"),_=a.get("icon");f=_||f||"roundRect";var b=Kee(f,a,l,u,d,m,h),x=new wc,w=a.getModel("textStyle");if(Ye(r.getLegendIcon)&&(!_||_==="inherit"))x.add(r.getLegendIcon({itemWidth:v,itemHeight:p,icon:f,iconRotate:g,itemStyle:b.itemStyle,lineStyle:b.lineStyle,symbolKeepAspect:y}));else{var S=_==="inherit"&&r.getData().getVisual("symbol")?g==="inherit"?r.getData().getVisual("symbolRotate"):g:0;x.add(Xee({itemWidth:v,itemHeight:p,icon:f,iconRotate:S,itemStyle:b.itemStyle,lineStyle:b.lineStyle,symbolKeepAspect:y}))}var C=s==="left"?v+5:-5,M=s,A=o.get("formatter"),P=n;Ee(A)&&A?P=A.replace("{name}",n??""):Ye(A)&&(P=A(n));var E=m?w.getTextColor():a.get("inactiveColor");x.add(new mr({style:ha(w,{text:P,x:C,y:p/2,fill:E,align:M,verticalAlign:"middle"},{inheritColor:E})}));var L=new Gt({shape:x.getBoundingRect(),invisible:!0}),O=a.getModel("tooltip");return O.get("show")&&A0({el:L,componentModel:o,itemName:n,itemTooltipOption:O.option}),x.add(L),x.eachChild(function(N){N.silent=!0}),L.silent=!c,this.getContentGroup().add(x),um(x),x.__legendDataIndex=i,x},t.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getContentGroup(),u=this.getSelectorGroup();ru(r.get("orient"),l,r.get("itemGap"),i.width,i.height);var f=l.getBoundingRect(),c=[-f.x,-f.y];if(u.markRedraw(),l.markRedraw(),o){ru("horizontal",u,r.get("selectorItemGap",!0));var h=u.getBoundingRect(),d=[-h.x,-h.y],v=r.get("selectorButtonGap",!0),p=r.getOrient().index,m=p===0?"width":"height",g=p===0?"height":"width",y=p===0?"y":"x";s==="end"?d[p]+=f[m]+v:c[p]+=h[m]+v,d[1-p]+=f[g]/2-h[g]/2,u.x=d[0],u.y=d[1],l.x=c[0],l.y=c[1];var _={x:0,y:0};return _[m]=f[m]+v+h[m],_[g]=Math.max(f[g],h[g]),_[y]=Math.min(0,h[y]+d[1-p]),_}else return l.x=c[0],l.y=c[1],this.group.getBoundingRect()},t.prototype.remove=function(){this.getContentGroup().removeAll(),this._isFirstRender=!0},t.type="legend.plain",t}(Mi);function Kee(e,t,r,n,i,a,o){function s(m,g){m.lineWidth==="auto"&&(m.lineWidth=g.lineWidth>0?2:0),Hm(m,function(y,_){m[_]==="inherit"&&(m[_]=g[_])})}var l=t.getModel("itemStyle"),u=l.getItemStyle(),f=e.lastIndexOf("empty",0)===0?"fill":"stroke",c=l.getShallow("decal");u.decal=!c||c==="inherit"?n.decal:Mm(c,o),u.fill==="inherit"&&(u.fill=n[i]),u.stroke==="inherit"&&(u.stroke=n[f]),u.opacity==="inherit"&&(u.opacity=(i==="fill"?n:r).opacity),s(u,n);var h=t.getModel("lineStyle"),d=h.getLineStyle();if(s(d,r),u.fill==="auto"&&(u.fill=n.fill),u.stroke==="auto"&&(u.stroke=n.fill),d.stroke==="auto"&&(d.stroke=n.fill),!a){var v=t.get("inactiveBorderWidth"),p=u[f];u.lineWidth=v==="auto"?n.lineWidth>0&&p?2:0:u.lineWidth,u.fill=t.get("inactiveColor"),u.stroke=t.get("inactiveBorderColor"),d.stroke=h.get("inactiveColor"),d.lineWidth=h.get("inactiveWidth")}return{itemStyle:u,lineStyle:d}}function Xee(e){var t=e.icon||"roundRect",r=j0(t,0,0,e.itemWidth,e.itemHeight,e.itemStyle.fill,e.symbolKeepAspect);return r.setStyle(e.itemStyle),r.rotation=(e.iconRotate||0)*Math.PI/180,r.setOrigin([e.itemWidth/2,e.itemHeight/2]),t.indexOf("empty")>-1&&(r.style.stroke=r.style.fill,r.style.fill="#fff",r.style.lineWidth=2),r}function TC(e,t,r,n){Vm(e,t,r,n),r.dispatchAction({type:"legendToggleSelect",name:e??t}),zm(e,t,r,n)}function xD(e){for(var t=e.getZr().storage.getDisplayList(),r,n=0,i=t.length;ni[o],m=[-d.x,-d.y];n||(m[a]=f[u]);var g=[0,0],y=[-v.x,-v.y],_=Ze(r.get("pageButtonGap",!0),r.get("itemGap",!0));if(p){var b=r.get("pageButtonPosition",!0);b==="end"?y[a]+=i[o]-v[o]:g[a]+=v[o]+_}y[1-a]+=d[s]/2-v[s]/2,f.setPosition(m),c.setPosition(g),h.setPosition(y);var x={x:0,y:0};if(x[o]=p?i[o]:d[o],x[s]=Math.max(d[s],v[s]),x[l]=Math.min(0,v[l]+y[1-a]),c.__rectSize=i[o],p){var w={x:0,y:0};w[o]=Math.max(i[o]-v[o]-_,0),w[s]=x[s],c.setClipPath(new Gt({shape:w})),c.__rectSize=w[o]}else h.eachChild(function(C){C.attr({invisible:!0,silent:!0})});var S=this._getPageInfo(r);return S.pageIndex!=null&&Or(f,{x:S.contentPosition[0],y:S.contentPosition[1]},p?r:null),this._updatePageInfoView(r,S),x},t.prototype._pageGo=function(r,n,i){var a=this._getPageInfo(n)[r];a!=null&&i.dispatchAction({type:"legendScroll",scrollDataIndex:a,legendId:n.id})},t.prototype._updatePageInfoView=function(r,n){var i=this._controllerGroup;R(["pagePrev","pageNext"],function(f){var c=f+"DataIndex",h=n[c]!=null,d=i.childOfName(f);d&&(d.setStyle("fill",h?r.get("pageIconColor",!0):r.get("pageIconInactiveColor",!0)),d.cursor=h?"pointer":"default")});var a=i.childOfName("pageText"),o=r.get("pageFormatter"),s=n.pageIndex,l=s!=null?s+1:0,u=n.pageCount;a&&o&&a.setStyle("text",Ee(o)?o.replace("{current}",l==null?"":l+"").replace("{total}",u==null?"":u+""):o({current:l,total:u}))},t.prototype._getPageInfo=function(r){var n=r.get("scrollDataIndex",!0),i=this.getContentGroup(),a=this._containerGroup.__rectSize,o=r.getOrient().index,s=Rp[o],l=kp[o],u=this._findTargetItemIndex(n),f=i.children(),c=f[u],h=f.length,d=h?1:0,v={contentPosition:[i.x,i.y],pageCount:d,pageIndex:d-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!c)return v;var p=b(c);v.contentPosition[o]=-p.s;for(var m=u+1,g=p,y=p,_=null;m<=h;++m)_=b(f[m]),(!_&&y.e>g.s+a||_&&!x(_,g.s))&&(y.i>g.i?g=y:g=_,g&&(v.pageNextDataIndex==null&&(v.pageNextDataIndex=g.i),++v.pageCount)),y=_;for(var m=u-1,g=p,y=p,_=null;m>=-1;--m)_=b(f[m]),(!_||!x(y,_.s))&&g.i=S&&w.s<=S+a}},t.prototype._findTargetItemIndex=function(r){if(!this._showController)return 0;var n,i=this.getContentGroup(),a;return i.eachChild(function(o,s){var l=o.__legendDataIndex;a==null&&l!=null&&(a=s),l===r&&(n=s)}),n??a},t.type="legend.scroll",t}(CD);const rte=tte;function nte(e){e.registerAction("legendScroll","legendscroll",function(t,r){var n=t.scrollDataIndex;n!=null&&r.eachComponent({mainType:"legend",subType:"scroll",query:t},function(i){i.setScrollDataIndex(n)})})}function ite(e){va(TD),e.registerComponentModel(ete),e.registerComponentView(rte),nte(e)}function ate(e){va(TD),va(ite)}va([tJ,$Q,GJ,vQ,Uee,Vee,ate,_ee]);function ote(e,t,r){const n=Z0(document.getElementById(e),"macarons");n.showLoading();const i={title:{text:"Network Traffic",subtext:"today",left:"center"},tooltip:{trigger:"item",formatter:function(a){return Mu.fileSize(a.data.value)+" ("+a.percent+"%)"}},legend:{orient:"vertical",left:"left",data:["Traffic In","Traffic Out"]},series:[{type:"pie",radius:"55%",center:["50%","60%"],data:[{value:t,name:"Traffic In"},{value:r,name:"Traffic Out"}],emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:"rgba(0, 0, 0, 0.5)"}}}]};n.setOption(i),n.hideLoading()}function ste(e,t){const r=Z0(document.getElementById(e),"macarons");r.showLoading();const n={title:{text:"Proxies",subtext:"now",left:"center"},tooltip:{trigger:"item",formatter:function(i){return String(i.data.value)}},legend:{orient:"vertical",left:"left",data:[]},series:[{type:"pie",radius:"55%",center:["50%","60%"],data:[],emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:"rgba(0, 0, 0, 0.5)"}}}]};t.proxyTypeCount.tcp!=null&&t.proxyTypeCount.tcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.tcp,name:"TCP"}),n.legend.data.push("TCP")),t.proxyTypeCount.udp!=null&&t.proxyTypeCount.udp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.udp,name:"UDP"}),n.legend.data.push("UDP")),t.proxyTypeCount.http!=null&&t.proxyTypeCount.http!=0&&(n.series[0].data.push({value:t.proxyTypeCount.http,name:"HTTP"}),n.legend.data.push("HTTP")),t.proxyTypeCount.https!=null&&t.proxyTypeCount.https!=0&&(n.series[0].data.push({value:t.proxyTypeCount.https,name:"HTTPS"}),n.legend.data.push("HTTPS")),t.proxyTypeCount.stcp!=null&&t.proxyTypeCount.stcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.stcp,name:"STCP"}),n.legend.data.push("STCP")),t.proxyTypeCount.sudp!=null&&t.proxyTypeCount.sudp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.sudp,name:"SUDP"}),n.legend.data.push("SUDP")),t.proxyTypeCount.xtcp!=null&&t.proxyTypeCount.xtcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.xtcp,name:"XTCP"}),n.legend.data.push("XTCP")),r.setOption(n),r.hideLoading()}function lte(e,t,r){const n={width:"600px",height:"400px"},i=Z0(document.getElementById(e),"macarons",n);i.showLoading(),t=t.reverse(),r=r.reverse();let a=new Date;a=new Date(a.getFullYear(),a.getMonth(),a.getDate()-6);const o=[];for(let l=0;l<7;l++)o.push(a.getFullYear()+"-"+(a.getMonth()+1)+"-"+a.getDate()),a=new Date(a.getFullYear(),a.getMonth(),a.getDate()+1);const s={tooltip:{trigger:"axis",axisPointer:{type:"shadow"},formatter:function(l){let u="";l.length>0&&(u+=l[0].name+"
");for(const f of l){const c='';u+=c+f.seriesName+": "+Mu.fileSize(f.value)+"
"}return u}},legend:{data:["Traffic In","Traffic Out"]},grid:{left:"3%",right:"4%",bottom:"3%",containLabel:!0},xAxis:[{type:"category",data:o}],yAxis:[{type:"value",axisLabel:{formatter:function(l){return Mu.fileSize(l)}}}],series:[{name:"Traffic In",type:"bar",data:t},{name:"Traffic Out",type:"bar",data:r}]};i.setOption(s),i.hideLoading()}const PC=ie({__name:"LongSpan",props:{content:{},length:{}},setup(e){return(t,r)=>{const n=Hs;return G(),ce(ft,null,[Z(n,{content:t.content,placement:"top"},{default:j(()=>[qt(ee("span",null,be(t.content.slice(0,t.length))+"...",513),[[Kn,t.content.length>t.length]])]),_:1},8,["content"]),qt(ee("span",null,be(t.content),513),[[Kn,t.content.length<30]])],64)}}}),ute={class:"source"},fte=ee("div",{id:"traffic",style:{width:"400px",height:"250px","margin-bottom":"30px"}},null,-1),cte=ee("div",{id:"proxies",style:{width:"400px",height:"250px"}},null,-1),dte=ie({__name:"ServerOverview",setup(e){let t=$({version:"",bindPort:0,kcpBindPort:0,quicBindPort:0,vhostHTTPPort:0,vhostHTTPSPort:0,tcpmuxHTTPConnectPort:0,subdomainHost:"",maxPoolCount:0,maxPortsPerClient:"",allowPortsStr:"",tlsForce:!1,heartbeatTimeout:0,clientCounts:0,curConns:0,proxyCounts:0});return(()=>{fetch("../api/serverinfo",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value.version=n.version,t.value.bindPort=n.bindPort,t.value.kcpBindPort=n.kcpBindPort,t.value.quicBindPort=n.quicBindPort,t.value.vhostHTTPPort=n.vhostHTTPPort,t.value.vhostHTTPSPort=n.vhostHTTPSPort,t.value.tcpmuxHTTPConnectPort=n.tcpmuxHTTPConnectPort,t.value.subdomainHost=n.subdomainHost,t.value.maxPoolCount=n.maxPoolCount,t.value.maxPortsPerClient=n.maxPortsPerClient,t.value.maxPortsPerClient=="0"&&(t.value.maxPortsPerClient="no limit"),t.value.allowPortsStr=n.allowPortsStr,t.value.tlsForce=n.tlsForce,t.value.heartbeatTimeout=n.heartbeatTimeout,t.value.clientCounts=n.clientCounts,t.value.curConns=n.curConns,t.value.proxyCounts=0,n.proxyTypeCount!=null&&(n.proxyTypeCount.tcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.tcp),n.proxyTypeCount.udp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.udp),n.proxyTypeCount.http!=null&&(t.value.proxyCounts+=n.proxyTypeCount.http),n.proxyTypeCount.https!=null&&(t.value.proxyCounts+=n.proxyTypeCount.https),n.proxyTypeCount.stcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.stcp),n.proxyTypeCount.sudp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.sudp),n.proxyTypeCount.xtcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.xtcp)),ote("traffic",n.totalTrafficIn,n.totalTrafficOut),ste("proxies",n)}).catch(()=>{Wl({showClose:!0,message:"Get server info from frps failed!",type:"warning"})})})(),(n,i)=>{const a=sA,o=oA,s=$A,l=FA;return G(),ce("div",null,[Z(l,null,{default:j(()=>[Z(s,{md:12},{default:j(()=>[ee("div",ute,[Z(o,{"label-position":"left","label-width":"220px",class:"server_info"},{default:j(()=>[Z(a,{label:"Version"},{default:j(()=>[ee("span",null,be(T(t).version),1)]),_:1}),Z(a,{label:"BindPort"},{default:j(()=>[ee("span",null,be(T(t).bindPort),1)]),_:1}),T(t).kcpBindPort!=0?(G(),de(a,{key:0,label:"KCP Bind Port"},{default:j(()=>[ee("span",null,be(T(t).kcpBindPort),1)]),_:1})):Ae("",!0),T(t).quicBindPort!=0?(G(),de(a,{key:1,label:"QUIC Bind Port"},{default:j(()=>[ee("span",null,be(T(t).quicBindPort),1)]),_:1})):Ae("",!0),T(t).vhostHTTPPort!=0?(G(),de(a,{key:2,label:"Http Port"},{default:j(()=>[ee("span",null,be(T(t).vhostHTTPPort),1)]),_:1})):Ae("",!0),T(t).vhostHTTPSPort!=0?(G(),de(a,{key:3,label:"Https Port"},{default:j(()=>[ee("span",null,be(T(t).vhostHTTPSPort),1)]),_:1})):Ae("",!0),T(t).tcpmuxHTTPConnectPort!=0?(G(),de(a,{key:4,label:"TCPMux HTTPConnect Port"},{default:j(()=>[ee("span",null,be(T(t).tcpmuxHTTPConnectPort),1)]),_:1})):Ae("",!0),T(t).subdomainHost!=""?(G(),de(a,{key:5,label:"Subdomain Host"},{default:j(()=>[Z(PC,{content:T(t).subdomainHost,length:30},null,8,["content"])]),_:1})):Ae("",!0),Z(a,{label:"Max PoolCount"},{default:j(()=>[ee("span",null,be(T(t).maxPoolCount),1)]),_:1}),Z(a,{label:"Max Ports Per Client"},{default:j(()=>[ee("span",null,be(T(t).maxPortsPerClient),1)]),_:1}),T(t).allowPortsStr!=""?(G(),de(a,{key:6,label:"Allow Ports"},{default:j(()=>[Z(PC,{content:T(t).allowPortsStr,length:30},null,8,["content"])]),_:1})):Ae("",!0),T(t).tlsForce===!0?(G(),de(a,{key:7,label:"TLS Force"},{default:j(()=>[ee("span",null,be(T(t).tlsForce),1)]),_:1})):Ae("",!0),Z(a,{label:"HeartBeat Timeout"},{default:j(()=>[ee("span",null,be(T(t).heartbeatTimeout),1)]),_:1}),Z(a,{label:"Client Counts"},{default:j(()=>[ee("span",null,be(T(t).clientCounts),1)]),_:1}),Z(a,{label:"Current Connections"},{default:j(()=>[ee("span",null,be(T(t).curConns),1)]),_:1}),Z(a,{label:"Proxy Counts"},{default:j(()=>[ee("span",null,be(T(t).proxyCounts),1)]),_:1})]),_:1})])]),_:1}),Z(s,{md:12},{default:j(()=>[fte,cte]),_:1})]),_:1})])}}});class qs{constructor(t){zt(this,"name");zt(this,"type");zt(this,"annotations");zt(this,"encryption");zt(this,"compression");zt(this,"conns");zt(this,"trafficIn");zt(this,"trafficOut");zt(this,"lastStartTime");zt(this,"lastCloseTime");zt(this,"status");zt(this,"clientVersion");zt(this,"addr");zt(this,"port");zt(this,"customDomains");zt(this,"hostHeaderRewrite");zt(this,"locations");zt(this,"subdomain");var r,n,i,a,o;if(this.name=t.name,this.type="",this.annotations=new Map,(r=t.conf)!=null&&r.annotations)for(const s in t.conf.annotations)this.annotations.set(s,t.conf.annotations[s]);this.encryption=!1,this.compression=!1,this.encryption=((i=(n=t.conf)==null?void 0:n.transport)==null?void 0:i.useEncryption)||this.encryption,this.compression=((o=(a=t.conf)==null?void 0:a.transport)==null?void 0:o.useCompression)||this.compression,this.conns=t.curConns,this.trafficIn=t.todayTrafficIn,this.trafficOut=t.todayTrafficOut,this.lastStartTime=t.lastStartTime,this.lastCloseTime=t.lastCloseTime,this.status=t.status,this.clientVersion=t.clientVersion,this.addr="",this.port=0,this.customDomains="",this.hostHeaderRewrite="",this.locations="",this.subdomain=""}}class hte extends qs{constructor(t){super(t),this.type="tcp",t.conf!=null?(this.addr=":"+t.conf.remotePort,this.port=t.conf.remotePort):(this.addr="",this.port=0)}}class vte extends qs{constructor(t){super(t),this.type="udp",t.conf!=null?(this.addr=":"+t.conf.remotePort,this.port=t.conf.remotePort):(this.addr="",this.port=0)}}class pte extends qs{constructor(t,r,n){super(t),this.type="http",this.port=r,t.conf&&(this.customDomains=t.conf.customDomains||this.customDomains,this.hostHeaderRewrite=t.conf.hostHeaderRewrite,this.locations=t.conf.locations,t.conf.subdomain&&(this.subdomain=`${t.conf.subdomain}.${n}`))}}class gte extends qs{constructor(t,r,n){super(t),this.type="https",this.port=r,t.conf!=null&&(this.customDomains=t.conf.customDomains||this.customDomains,t.conf.subdomain&&(this.subdomain=`${t.conf.subdomain}.${n}`))}}class mte extends qs{constructor(t){super(t),this.type="stcp"}}class yte extends qs{constructor(t){super(t),this.type="sudp"}}const _te=["id"],bte=ie({__name:"Traffic",props:{proxyName:{}},setup(e){const t=e;return(()=>{let n="../api/traffic/"+t.proxyName;fetch(n,{credentials:"include"}).then(i=>i.json()).then(i=>{lte(t.proxyName,i.trafficIn,i.trafficOut)}).catch(i=>{Wl({showClose:!0,message:"Get traffic info failed!"+i,type:"warning"})})})(),(n,i)=>(G(),ce("div",{id:n.proxyName,style:{width:"600px",height:"400px"}},null,8,_te))}}),wte={key:2},Ste={class:"annotation-key"},xte=ie({__name:"ProxyViewExpand",props:{row:{},proxyType:{}},setup(e){const t=e,r=()=>{const n=[];return t.row.annotations&&t.row.annotations.forEach((i,a)=>{n.push({key:a,value:i})}),n};return(n,i)=>{const a=sA,o=oA,s=VA,l=wW;return G(),ce(ft,null,[n.proxyType==="http"||n.proxyType==="https"?(G(),de(o,{key:0,"label-position":"left",inline:"",class:"proxy-table-expand"},{default:j(()=>[Z(a,{label:"Name"},{default:j(()=>[ee("span",null,be(n.row.name),1)]),_:1}),Z(a,{label:"Type"},{default:j(()=>[ee("span",null,be(n.row.type),1)]),_:1}),Z(a,{label:"Domains"},{default:j(()=>[ee("span",null,be(n.row.customDomains),1)]),_:1}),Z(a,{label:"SubDomain"},{default:j(()=>[ee("span",null,be(n.row.subdomain),1)]),_:1}),Z(a,{label:"locations"},{default:j(()=>[ee("span",null,be(n.row.locations),1)]),_:1}),Z(a,{label:"HostRewrite"},{default:j(()=>[ee("span",null,be(n.row.hostHeaderRewrite),1)]),_:1}),Z(a,{label:"Encryption"},{default:j(()=>[ee("span",null,be(n.row.encryption),1)]),_:1}),Z(a,{label:"Compression"},{default:j(()=>[ee("span",null,be(n.row.compression),1)]),_:1}),Z(a,{label:"Last Start"},{default:j(()=>[ee("span",null,be(n.row.lastStartTime),1)]),_:1}),Z(a,{label:"Last Close"},{default:j(()=>[ee("span",null,be(n.row.lastCloseTime),1)]),_:1})]),_:1})):(G(),de(o,{key:1,"label-position":"left",inline:"",class:"proxy-table-expand"},{default:j(()=>[Z(a,{label:"Name"},{default:j(()=>[ee("span",null,be(n.row.name),1)]),_:1}),Z(a,{label:"Type"},{default:j(()=>[ee("span",null,be(n.row.type),1)]),_:1}),Z(a,{label:"Addr"},{default:j(()=>[ee("span",null,be(n.row.addr),1)]),_:1}),Z(a,{label:"Encryption"},{default:j(()=>[ee("span",null,be(n.row.encryption),1)]),_:1}),Z(a,{label:"Compression"},{default:j(()=>[ee("span",null,be(n.row.compression),1)]),_:1}),Z(a,{label:"Last Start"},{default:j(()=>[ee("span",null,be(n.row.lastStartTime),1)]),_:1}),Z(a,{label:"Last Close"},{default:j(()=>[ee("span",null,be(n.row.lastCloseTime),1)]),_:1})]),_:1})),n.row.annotations&&n.row.annotations.size>0?(G(),ce("div",wte,[Z(s),Z(l,{class:"title-text",size:"large"},{default:j(()=>[mt("Annotations")]),_:1}),ee("ul",null,[(G(!0),ce(ft,null,Hp(r(),u=>(G(),ce("li",null,[ee("span",Ste,be(u.key),1),ee("span",null,be(u.value),1)]))),256))])])):Ae("",!0)],64)}}}),Cte={class:"flex items-center",style:{"margin-right":"30px"}},Ks=ie({__name:"ProxyView",props:{proxies:{},proxyType:{}},emits:["refresh"],setup(e,{emit:t}){const r=t,n=$(!1),i=$(""),a=(l,u)=>Mu.fileSize(l.trafficIn),o=(l,u)=>Mu.fileSize(l.trafficOut),s=()=>{fetch("/api/proxies?status=offline",{method:"DELETE",credentials:"include"}).then(l=>{l.ok?(Wl({message:"Successfully cleared offline proxies",type:"success"}),r("refresh")):Wl({message:"Failed to clear offline proxies: "+l.status+" "+l.statusText,type:"warning"})}).catch(l=>{Wl({message:"Failed to clear offline proxies: "+l.message,type:"warning"})})};return(l,u)=>{const f=yg,c=eV,h=q6,d=gW,v=N8,p=pW,m=bte,g=p6;return G(),ce(ft,null,[ee("div",null,[Z(h,{icon:null,style:{width:"100%","margin-left":"30px","margin-bottom":"20px"}},{title:j(()=>[ee("span",null,be(l.proxyType),1)]),content:j(()=>[]),extra:j(()=>[ee("div",Cte,[Z(c,{title:"Are you sure to clear all data of offline proxies?",onConfirm:s},{reference:j(()=>[Z(f,null,{default:j(()=>[mt("ClearOfflineProxies")]),_:1})]),_:1}),Z(f,{onClick:u[0]||(u[0]=y=>l.$emit("refresh"))},{default:j(()=>[mt("Refresh")]),_:1})])]),_:1}),Z(p,{data:l.proxies,"default-sort":{prop:"name",order:"ascending"},style:{width:"100%"}},{default:j(()=>[Z(d,{type:"expand"},{default:j(y=>[Z(xte,{row:y.row,proxyType:l.proxyType},null,8,["row","proxyType"])]),_:1}),Z(d,{label:"Name",prop:"name",sortable:""}),Z(d,{label:"Port",prop:"port",sortable:""}),Z(d,{label:"Connections",prop:"conns",sortable:""}),Z(d,{label:"Traffic In",prop:"trafficIn",formatter:a,sortable:""}),Z(d,{label:"Traffic Out",prop:"trafficOut",formatter:o,sortable:""}),Z(d,{label:"ClientVersion",prop:"clientVersion",sortable:""}),Z(d,{label:"Status",prop:"status",sortable:""},{default:j(y=>[y.row.status==="online"?(G(),de(v,{key:0,type:"success"},{default:j(()=>[mt(be(y.row.status),1)]),_:2},1024)):(G(),de(v,{key:1,type:"danger"},{default:j(()=>[mt(be(y.row.status),1)]),_:2},1024))]),_:1}),Z(d,{label:"Operations"},{default:j(y=>[Z(f,{type:"primary",name:y.row.name,style:{"margin-bottom":"10px"},onClick:_=>{i.value=y.row.name,n.value=!0}},{default:j(()=>[mt("Traffic ")]),_:2},1032,["name","onClick"])]),_:1})]),_:1},8,["data"])]),Z(g,{modelValue:n.value,"onUpdate:modelValue":u[1]||(u[1]=y=>n.value=y),"destroy-on-close":"true",title:i.value,width:"700px"},{default:j(()=>[Z(m,{proxyName:i.value},null,8,["proxyName"])]),_:1},8,["modelValue","title"])],64)}}}),Tte=ie({__name:"ProxiesTCP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/tcp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new hte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"tcp",onRefresh:r},null,8,["proxies"]))}}),Mte=ie({__name:"ProxiesUDP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/udp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new vte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"udp",onRefresh:r},null,8,["proxies"]))}}),Ate=ie({__name:"ProxiesHTTP",setup(e){let t=$([]);const r=()=>{let n,i;fetch("../api/serverinfo",{credentials:"include"}).then(a=>a.json()).then(a=>{n=a.vhostHTTPPort,i=a.subdomainHost,!(n==null||n==0)&&fetch("../api/proxy/http",{credentials:"include"}).then(o=>o.json()).then(o=>{t.value=[];for(let s of o.proxies)t.value.push(new pte(s,n,i))})})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"http",onRefresh:r},null,8,["proxies"]))}}),Pte=ie({__name:"ProxiesHTTPS",setup(e){let t=$([]);const r=()=>{let n,i;fetch("../api/serverinfo",{credentials:"include"}).then(a=>a.json()).then(a=>{n=a.vhostHTTPSPort,i=a.subdomainHost,!(n==null||n==0)&&fetch("../api/proxy/https",{credentials:"include"}).then(o=>o.json()).then(o=>{t.value=[];for(let s of o.proxies)t.value.push(new gte(s,n,i))})})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"https",onRefresh:r},null,8,["proxies"]))}}),Ete=ie({__name:"ProxiesSTCP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/stcp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new mte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"stcp",onRefresh:r},null,8,["proxies"]))}}),Lte=ie({__name:"ProxiesSUDP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/sudp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new yte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"sudp",onRefresh:r},null,8,["proxies"]))}}),Dte=jG({history:uG(),routes:[{path:"/",name:"ServerOverview",component:dte},{path:"/proxies/tcp",name:"ProxiesTCP",component:Tte},{path:"/proxies/udp",name:"ProxiesUDP",component:Mte},{path:"/proxies/http",name:"ProxiesHTTP",component:Ate},{path:"/proxies/https",name:"ProxiesHTTPS",component:Pte},{path:"/proxies/stcp",name:"ProxiesSTCP",component:Ete},{path:"/proxies/sudp",name:"ProxiesSUDP",component:Lte}]}),MD=oR(GW);MD.use(Dte);MD.mount("#app")});export default Ite();
+`||u==="")){var f=o.isSelected(u);n.hasOwnProperty(u)?n[u]=n[u]&&f:n[u]=f}})}),e==="allSelect"||e==="inverseSelect"?{selected:n}:{name:t.name,selected:n}}function Qee(e){e.registerAction("legendToggleSelect","legendselectchanged",Ot(wl,"toggleSelected")),e.registerAction("legendAllSelect","legendselectall",Ot(wl,"allSelect")),e.registerAction("legendInverseSelect","legendinverseselect",Ot(wl,"inverseSelect")),e.registerAction("legendSelect","legendselected",Ot(wl,"select")),e.registerAction("legendUnSelect","legendunselected",Ot(wl,"unSelect"))}function TD(e){e.registerComponentModel($m),e.registerComponentView(CD),e.registerProcessor(e.PRIORITY.PROCESSOR.SERIES_FILTER,Zee),e.registerSubTypeDefaulter("legend",function(){return"plain"}),Qee(e)}var Jee=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r}return t.prototype.setScrollDataIndex=function(r){this.option.scrollDataIndex=r},t.prototype.init=function(r,n,i){var a=Rh(r);e.prototype.init.call(this,r,n,i),MC(this,r,a)},t.prototype.mergeOption=function(r,n){e.prototype.mergeOption.call(this,r,n),MC(this,this.option,r)},t.type="legend.scroll",t.defaultOption=rE($m.defaultOption,{scrollDataIndex:0,pageButtonItemGap:5,pageButtonGap:null,pageButtonPosition:"end",pageFormatter:"{current}/{total}",pageIcons:{horizontal:["M0,0L12,-10L12,10z","M0,0L-12,-10L-12,10z"],vertical:["M0,0L20,0L10,-20z","M0,0L20,0L10,20z"]},pageIconColor:"#2f4554",pageIconInactiveColor:"#aaa",pageIconSize:15,pageTextStyle:{color:"#333"},animationDurationUpdate:800}),t}($m);function MC(e,t,r){var n=e.getOrient(),i=[1,1];i[n.index]=0,Ds(t,r,{type:"box",ignoreSize:!!i})}const ete=Jee;var AC=Ur,Rp=["width","height"],kp=["x","y"],tte=function(e){ge(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.type=t.type,r.newlineDisabled=!0,r._currentIndex=0,r}return t.prototype.init=function(){e.prototype.init.call(this),this.group.add(this._containerGroup=new AC),this._containerGroup.add(this.getContentGroup()),this.group.add(this._controllerGroup=new AC)},t.prototype.resetInner=function(){e.prototype.resetInner.call(this),this._controllerGroup.removeAll(),this._containerGroup.removeClipPath(),this._containerGroup.__rectSize=null},t.prototype.renderInner=function(r,n,i,a,o,s,l){var u=this;e.prototype.renderInner.call(this,r,n,i,a,o,s,l);var f=this._controllerGroup,c=n.get("pageIconSize",!0),h=ye(c)?c:[c,c];v("pagePrev",0);var d=n.getModel("pageTextStyle");f.add(new mr({name:"pageText",style:{text:"xx/xx",fill:d.getTextColor(),font:d.getFont(),verticalAlign:"middle",align:"center"},silent:!0})),v("pageNext",1);function v(p,m){var g=p+"DataIndex",y=M0(n.get("pageIcons",!0)[n.getOrient().name][m],{onclick:xt(u._pageGo,u,g,n,a)},{x:-h[0]/2,y:-h[1]/2,width:h[0],height:h[1]});y.name=p,f.add(y)}},t.prototype.layoutInner=function(r,n,i,a,o,s){var l=this.getSelectorGroup(),u=r.getOrient().index,f=Rp[u],c=kp[u],h=Rp[1-u],d=kp[1-u];o&&ru("horizontal",l,r.get("selectorItemGap",!0));var v=r.get("selectorButtonGap",!0),p=l.getBoundingRect(),m=[-p.x,-p.y],g=tt(i);o&&(g[f]=i[f]-p[f]-v);var y=this._layoutContentAndController(r,a,g,u,f,h,d,c);if(o){if(s==="end")m[u]+=y[f]+v;else{var _=p[f]+v;m[u]-=_,y[c]-=_}y[f]+=p[f]+v,m[1-u]+=y[d]+y[h]/2-p[h]/2,y[h]=Math.max(y[h],p[h]),y[d]=Math.min(y[d],p[d]+m[1-u]),l.x=m[0],l.y=m[1],l.markRedraw()}return y},t.prototype._layoutContentAndController=function(r,n,i,a,o,s,l,u){var f=this.getContentGroup(),c=this._containerGroup,h=this._controllerGroup;ru(r.get("orient"),f,r.get("itemGap"),a?i.width:null,a?null:i.height),ru("horizontal",h,r.get("pageButtonItemGap",!0));var d=f.getBoundingRect(),v=h.getBoundingRect(),p=this._showController=d[o]>i[o],m=[-d.x,-d.y];n||(m[a]=f[u]);var g=[0,0],y=[-v.x,-v.y],_=Ze(r.get("pageButtonGap",!0),r.get("itemGap",!0));if(p){var b=r.get("pageButtonPosition",!0);b==="end"?y[a]+=i[o]-v[o]:g[a]+=v[o]+_}y[1-a]+=d[s]/2-v[s]/2,f.setPosition(m),c.setPosition(g),h.setPosition(y);var x={x:0,y:0};if(x[o]=p?i[o]:d[o],x[s]=Math.max(d[s],v[s]),x[l]=Math.min(0,v[l]+y[1-a]),c.__rectSize=i[o],p){var w={x:0,y:0};w[o]=Math.max(i[o]-v[o]-_,0),w[s]=x[s],c.setClipPath(new Gt({shape:w})),c.__rectSize=w[o]}else h.eachChild(function(C){C.attr({invisible:!0,silent:!0})});var S=this._getPageInfo(r);return S.pageIndex!=null&&Or(f,{x:S.contentPosition[0],y:S.contentPosition[1]},p?r:null),this._updatePageInfoView(r,S),x},t.prototype._pageGo=function(r,n,i){var a=this._getPageInfo(n)[r];a!=null&&i.dispatchAction({type:"legendScroll",scrollDataIndex:a,legendId:n.id})},t.prototype._updatePageInfoView=function(r,n){var i=this._controllerGroup;R(["pagePrev","pageNext"],function(f){var c=f+"DataIndex",h=n[c]!=null,d=i.childOfName(f);d&&(d.setStyle("fill",h?r.get("pageIconColor",!0):r.get("pageIconInactiveColor",!0)),d.cursor=h?"pointer":"default")});var a=i.childOfName("pageText"),o=r.get("pageFormatter"),s=n.pageIndex,l=s!=null?s+1:0,u=n.pageCount;a&&o&&a.setStyle("text",Ee(o)?o.replace("{current}",l==null?"":l+"").replace("{total}",u==null?"":u+""):o({current:l,total:u}))},t.prototype._getPageInfo=function(r){var n=r.get("scrollDataIndex",!0),i=this.getContentGroup(),a=this._containerGroup.__rectSize,o=r.getOrient().index,s=Rp[o],l=kp[o],u=this._findTargetItemIndex(n),f=i.children(),c=f[u],h=f.length,d=h?1:0,v={contentPosition:[i.x,i.y],pageCount:d,pageIndex:d-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!c)return v;var p=b(c);v.contentPosition[o]=-p.s;for(var m=u+1,g=p,y=p,_=null;m<=h;++m)_=b(f[m]),(!_&&y.e>g.s+a||_&&!x(_,g.s))&&(y.i>g.i?g=y:g=_,g&&(v.pageNextDataIndex==null&&(v.pageNextDataIndex=g.i),++v.pageCount)),y=_;for(var m=u-1,g=p,y=p,_=null;m>=-1;--m)_=b(f[m]),(!_||!x(y,_.s))&&g.i=S&&w.s<=S+a}},t.prototype._findTargetItemIndex=function(r){if(!this._showController)return 0;var n,i=this.getContentGroup(),a;return i.eachChild(function(o,s){var l=o.__legendDataIndex;a==null&&l!=null&&(a=s),l===r&&(n=s)}),n??a},t.type="legend.scroll",t}(CD);const rte=tte;function nte(e){e.registerAction("legendScroll","legendscroll",function(t,r){var n=t.scrollDataIndex;n!=null&&r.eachComponent({mainType:"legend",subType:"scroll",query:t},function(i){i.setScrollDataIndex(n)})})}function ite(e){va(TD),e.registerComponentModel(ete),e.registerComponentView(rte),nte(e)}function ate(e){va(TD),va(ite)}va([tJ,$Q,GJ,vQ,Uee,Vee,ate,_ee]);function ote(e,t,r){const n=Z0(document.getElementById(e),"macarons");n.showLoading();const i={title:{text:"Network Traffic",subtext:"today",left:"center"},tooltip:{trigger:"item",formatter:function(a){return Mu.fileSize(a.data.value)+" ("+a.percent+"%)"}},legend:{orient:"vertical",left:"left",data:["Traffic In","Traffic Out"]},series:[{type:"pie",radius:"55%",center:["50%","60%"],data:[{value:t,name:"Traffic In"},{value:r,name:"Traffic Out"}],emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:"rgba(0, 0, 0, 0.5)"}}}]};n.setOption(i),n.hideLoading()}function ste(e,t){const r=Z0(document.getElementById(e),"macarons");r.showLoading();const n={title:{text:"Proxies",subtext:"now",left:"center"},tooltip:{trigger:"item",formatter:function(i){return String(i.data.value)}},legend:{orient:"vertical",left:"left",data:[]},series:[{type:"pie",radius:"55%",center:["50%","60%"],data:[],emphasis:{itemStyle:{shadowBlur:10,shadowOffsetX:0,shadowColor:"rgba(0, 0, 0, 0.5)"}}}]};t.proxyTypeCount.tcp!=null&&t.proxyTypeCount.tcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.tcp,name:"TCP"}),n.legend.data.push("TCP")),t.proxyTypeCount.udp!=null&&t.proxyTypeCount.udp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.udp,name:"UDP"}),n.legend.data.push("UDP")),t.proxyTypeCount.http!=null&&t.proxyTypeCount.http!=0&&(n.series[0].data.push({value:t.proxyTypeCount.http,name:"HTTP"}),n.legend.data.push("HTTP")),t.proxyTypeCount.https!=null&&t.proxyTypeCount.https!=0&&(n.series[0].data.push({value:t.proxyTypeCount.https,name:"HTTPS"}),n.legend.data.push("HTTPS")),t.proxyTypeCount.stcp!=null&&t.proxyTypeCount.stcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.stcp,name:"STCP"}),n.legend.data.push("STCP")),t.proxyTypeCount.sudp!=null&&t.proxyTypeCount.sudp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.sudp,name:"SUDP"}),n.legend.data.push("SUDP")),t.proxyTypeCount.xtcp!=null&&t.proxyTypeCount.xtcp!=0&&(n.series[0].data.push({value:t.proxyTypeCount.xtcp,name:"XTCP"}),n.legend.data.push("XTCP")),r.setOption(n),r.hideLoading()}function lte(e,t,r){const n={width:"600px",height:"400px"},i=Z0(document.getElementById(e),"macarons",n);i.showLoading(),t=t.reverse(),r=r.reverse();let a=new Date;a=new Date(a.getFullYear(),a.getMonth(),a.getDate()-6);const o=[];for(let l=0;l<7;l++)o.push(a.getFullYear()+"-"+(a.getMonth()+1)+"-"+a.getDate()),a=new Date(a.getFullYear(),a.getMonth(),a.getDate()+1);const s={tooltip:{trigger:"axis",axisPointer:{type:"shadow"},formatter:function(l){let u="";l.length>0&&(u+=l[0].name+"
");for(const f of l){const c='';u+=c+f.seriesName+": "+Mu.fileSize(f.value)+"
"}return u}},legend:{data:["Traffic In","Traffic Out"]},grid:{left:"3%",right:"4%",bottom:"3%",containLabel:!0},xAxis:[{type:"category",data:o}],yAxis:[{type:"value",axisLabel:{formatter:function(l){return Mu.fileSize(l)}}}],series:[{name:"Traffic In",type:"bar",data:t},{name:"Traffic Out",type:"bar",data:r}]};i.setOption(s),i.hideLoading()}const PC=ie({__name:"LongSpan",props:{content:{},length:{}},setup(e){return(t,r)=>{const n=Hs;return G(),ce(ft,null,[Z(n,{content:t.content,placement:"top"},{default:j(()=>[qt(ee("span",null,be(t.content.slice(0,t.length))+"...",513),[[Kn,t.content.length>t.length]])]),_:1},8,["content"]),qt(ee("span",null,be(t.content),513),[[Kn,t.content.length<30]])],64)}}}),ute={class:"source"},fte=ee("div",{id:"traffic",style:{width:"400px",height:"250px","margin-bottom":"30px"}},null,-1),cte=ee("div",{id:"proxies",style:{width:"400px",height:"250px"}},null,-1),dte=ie({__name:"ServerOverview",setup(e){let t=$({version:"",bindPort:0,kcpBindPort:0,quicBindPort:0,vhostHTTPPort:0,vhostHTTPSPort:0,tcpmuxHTTPConnectPort:0,subdomainHost:"",maxPoolCount:0,maxPortsPerClient:"",allowPortsStr:"",tlsForce:!1,heartbeatTimeout:0,clientCounts:0,curConns:0,proxyCounts:0});return(()=>{fetch("../api/serverinfo",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value.version=n.version,t.value.bindPort=n.bindPort,t.value.kcpBindPort=n.kcpBindPort,t.value.quicBindPort=n.quicBindPort,t.value.vhostHTTPPort=n.vhostHTTPPort,t.value.vhostHTTPSPort=n.vhostHTTPSPort,t.value.tcpmuxHTTPConnectPort=n.tcpmuxHTTPConnectPort,t.value.subdomainHost=n.subdomainHost,t.value.maxPoolCount=n.maxPoolCount,t.value.maxPortsPerClient=n.maxPortsPerClient,t.value.maxPortsPerClient=="0"&&(t.value.maxPortsPerClient="no limit"),t.value.allowPortsStr=n.allowPortsStr,t.value.tlsForce=n.tlsForce,t.value.heartbeatTimeout=n.heartbeatTimeout,t.value.clientCounts=n.clientCounts,t.value.curConns=n.curConns,t.value.proxyCounts=0,n.proxyTypeCount!=null&&(n.proxyTypeCount.tcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.tcp),n.proxyTypeCount.udp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.udp),n.proxyTypeCount.http!=null&&(t.value.proxyCounts+=n.proxyTypeCount.http),n.proxyTypeCount.https!=null&&(t.value.proxyCounts+=n.proxyTypeCount.https),n.proxyTypeCount.stcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.stcp),n.proxyTypeCount.sudp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.sudp),n.proxyTypeCount.xtcp!=null&&(t.value.proxyCounts+=n.proxyTypeCount.xtcp)),ote("traffic",n.totalTrafficIn,n.totalTrafficOut),ste("proxies",n)}).catch(()=>{Wl({showClose:!0,message:"Get server info from frps failed!",type:"warning"})})})(),(n,i)=>{const a=sA,o=oA,s=$A,l=FA;return G(),ce("div",null,[Z(l,null,{default:j(()=>[Z(s,{md:12},{default:j(()=>[ee("div",ute,[Z(o,{"label-position":"left","label-width":"220px",class:"server_info"},{default:j(()=>[Z(a,{label:"Version"},{default:j(()=>[ee("span",null,be(T(t).version),1)]),_:1}),Z(a,{label:"BindPort"},{default:j(()=>[ee("span",null,be(T(t).bindPort),1)]),_:1}),T(t).kcpBindPort!=0?(G(),de(a,{key:0,label:"KCP Bind Port"},{default:j(()=>[ee("span",null,be(T(t).kcpBindPort),1)]),_:1})):Ae("",!0),T(t).quicBindPort!=0?(G(),de(a,{key:1,label:"QUIC Bind Port"},{default:j(()=>[ee("span",null,be(T(t).quicBindPort),1)]),_:1})):Ae("",!0),T(t).vhostHTTPPort!=0?(G(),de(a,{key:2,label:"Http Port"},{default:j(()=>[ee("span",null,be(T(t).vhostHTTPPort),1)]),_:1})):Ae("",!0),T(t).vhostHTTPSPort!=0?(G(),de(a,{key:3,label:"Https Port"},{default:j(()=>[ee("span",null,be(T(t).vhostHTTPSPort),1)]),_:1})):Ae("",!0),T(t).tcpmuxHTTPConnectPort!=0?(G(),de(a,{key:4,label:"TCPMux HTTPConnect Port"},{default:j(()=>[ee("span",null,be(T(t).tcpmuxHTTPConnectPort),1)]),_:1})):Ae("",!0),T(t).subdomainHost!=""?(G(),de(a,{key:5,label:"Subdomain Host"},{default:j(()=>[Z(PC,{content:T(t).subdomainHost,length:30},null,8,["content"])]),_:1})):Ae("",!0),Z(a,{label:"Max PoolCount"},{default:j(()=>[ee("span",null,be(T(t).maxPoolCount),1)]),_:1}),Z(a,{label:"Max Ports Per Client"},{default:j(()=>[ee("span",null,be(T(t).maxPortsPerClient),1)]),_:1}),T(t).allowPortsStr!=""?(G(),de(a,{key:6,label:"Allow Ports"},{default:j(()=>[Z(PC,{content:T(t).allowPortsStr,length:30},null,8,["content"])]),_:1})):Ae("",!0),T(t).tlsForce===!0?(G(),de(a,{key:7,label:"TLS Force"},{default:j(()=>[ee("span",null,be(T(t).tlsForce),1)]),_:1})):Ae("",!0),Z(a,{label:"HeartBeat Timeout"},{default:j(()=>[ee("span",null,be(T(t).heartbeatTimeout),1)]),_:1}),Z(a,{label:"Client Counts"},{default:j(()=>[ee("span",null,be(T(t).clientCounts),1)]),_:1}),Z(a,{label:"Current Connections"},{default:j(()=>[ee("span",null,be(T(t).curConns),1)]),_:1}),Z(a,{label:"Proxy Counts"},{default:j(()=>[ee("span",null,be(T(t).proxyCounts),1)]),_:1})]),_:1})])]),_:1}),Z(s,{md:12},{default:j(()=>[fte,cte]),_:1})]),_:1})])}}});class qs{constructor(t){zt(this,"name");zt(this,"type");zt(this,"annotations");zt(this,"encryption");zt(this,"compression");zt(this,"conns");zt(this,"trafficIn");zt(this,"trafficOut");zt(this,"lastStartTime");zt(this,"lastCloseTime");zt(this,"status");zt(this,"clientVersion");zt(this,"addr");zt(this,"port");zt(this,"customDomains");zt(this,"hostHeaderRewrite");zt(this,"locations");zt(this,"subdomain");var r,n,i,a,o;if(this.name=t.name,this.type="",this.annotations=new Map,(r=t.conf)!=null&&r.annotations)for(const s in t.conf.annotations)this.annotations.set(s,t.conf.annotations[s]);this.encryption=!1,this.compression=!1,this.encryption=((i=(n=t.conf)==null?void 0:n.transport)==null?void 0:i.useEncryption)||this.encryption,this.compression=((o=(a=t.conf)==null?void 0:a.transport)==null?void 0:o.useCompression)||this.compression,this.conns=t.curConns,this.trafficIn=t.todayTrafficIn,this.trafficOut=t.todayTrafficOut,this.lastStartTime=t.lastStartTime,this.lastCloseTime=t.lastCloseTime,this.status=t.status,this.clientVersion=t.clientVersion,this.addr="",this.port=0,this.customDomains="",this.hostHeaderRewrite="",this.locations="",this.subdomain=""}}class hte extends qs{constructor(t){super(t),this.type="tcp",t.conf!=null?(this.addr=":"+t.conf.remotePort,this.port=t.conf.remotePort):(this.addr="",this.port=0)}}class vte extends qs{constructor(t){super(t),this.type="udp",t.conf!=null?(this.addr=":"+t.conf.remotePort,this.port=t.conf.remotePort):(this.addr="",this.port=0)}}class pte extends qs{constructor(t,r,n){super(t),this.type="http",this.port=r,t.conf&&(this.customDomains=t.conf.customDomains||this.customDomains,this.hostHeaderRewrite=t.conf.hostHeaderRewrite,this.locations=t.conf.locations,t.conf.subdomain&&(this.subdomain=`${t.conf.subdomain}.${n}`))}}class gte extends qs{constructor(t,r,n){super(t),this.type="https",this.port=r,t.conf!=null&&(this.customDomains=t.conf.customDomains||this.customDomains,t.conf.subdomain&&(this.subdomain=`${t.conf.subdomain}.${n}`))}}class mte extends qs{constructor(t){super(t),this.type="stcp"}}class yte extends qs{constructor(t){super(t),this.type="sudp"}}const _te=["id"],bte=ie({__name:"Traffic",props:{proxyName:{}},setup(e){const t=e;return(()=>{let n="../api/traffic/"+t.proxyName;fetch(n,{credentials:"include"}).then(i=>i.json()).then(i=>{lte(t.proxyName,i.trafficIn,i.trafficOut)}).catch(i=>{Wl({showClose:!0,message:"Get traffic info failed!"+i,type:"warning"})})})(),(n,i)=>(G(),ce("div",{id:n.proxyName,style:{width:"600px",height:"400px"}},null,8,_te))}}),wte={key:2},Ste={class:"annotation-key"},xte=ie({__name:"ProxyViewExpand",props:{row:{},proxyType:{}},setup(e){const t=e,r=()=>{const n=[];return t.row.annotations&&t.row.annotations.forEach((i,a)=>{n.push({key:a,value:i})}),n};return(n,i)=>{const a=sA,o=oA,s=VA,l=wW;return G(),ce(ft,null,[n.proxyType==="http"||n.proxyType==="https"?(G(),de(o,{key:0,"label-position":"left",inline:"",class:"proxy-table-expand"},{default:j(()=>[Z(a,{label:"Name"},{default:j(()=>[ee("span",null,be(n.row.name),1)]),_:1}),Z(a,{label:"Type"},{default:j(()=>[ee("span",null,be(n.row.type),1)]),_:1}),Z(a,{label:"Domains"},{default:j(()=>[ee("span",null,be(n.row.customDomains),1)]),_:1}),Z(a,{label:"SubDomain"},{default:j(()=>[ee("span",null,be(n.row.subdomain),1)]),_:1}),Z(a,{label:"locations"},{default:j(()=>[ee("span",null,be(n.row.locations),1)]),_:1}),Z(a,{label:"HostRewrite"},{default:j(()=>[ee("span",null,be(n.row.hostHeaderRewrite),1)]),_:1}),Z(a,{label:"Encryption"},{default:j(()=>[ee("span",null,be(n.row.encryption),1)]),_:1}),Z(a,{label:"Compression"},{default:j(()=>[ee("span",null,be(n.row.compression),1)]),_:1}),Z(a,{label:"Last Start"},{default:j(()=>[ee("span",null,be(n.row.lastStartTime),1)]),_:1}),Z(a,{label:"Last Close"},{default:j(()=>[ee("span",null,be(n.row.lastCloseTime),1)]),_:1})]),_:1})):(G(),de(o,{key:1,"label-position":"left",inline:"",class:"proxy-table-expand"},{default:j(()=>[Z(a,{label:"Name"},{default:j(()=>[ee("span",null,be(n.row.name),1)]),_:1}),Z(a,{label:"Type"},{default:j(()=>[ee("span",null,be(n.row.type),1)]),_:1}),Z(a,{label:"Addr"},{default:j(()=>[ee("span",null,be(n.row.addr),1)]),_:1}),Z(a,{label:"Encryption"},{default:j(()=>[ee("span",null,be(n.row.encryption),1)]),_:1}),Z(a,{label:"Compression"},{default:j(()=>[ee("span",null,be(n.row.compression),1)]),_:1}),Z(a,{label:"Last Start"},{default:j(()=>[ee("span",null,be(n.row.lastStartTime),1)]),_:1}),Z(a,{label:"Last Close"},{default:j(()=>[ee("span",null,be(n.row.lastCloseTime),1)]),_:1})]),_:1})),n.row.annotations&&n.row.annotations.size>0?(G(),ce("div",wte,[Z(s),Z(l,{class:"title-text",size:"large"},{default:j(()=>[mt("Annotations")]),_:1}),ee("ul",null,[(G(!0),ce(ft,null,Hp(r(),u=>(G(),ce("li",null,[ee("span",Ste,be(u.key),1),ee("span",null,be(u.value),1)]))),256))])])):Ae("",!0)],64)}}}),Cte={class:"flex items-center",style:{"margin-right":"30px"}},Ks=ie({__name:"ProxyView",props:{proxies:{},proxyType:{}},emits:["refresh"],setup(e,{emit:t}){const r=t,n=$(!1),i=$(""),a=(l,u)=>Mu.fileSize(l.trafficIn),o=(l,u)=>Mu.fileSize(l.trafficOut),s=()=>{fetch("../api/proxies?status=offline",{method:"DELETE",credentials:"include"}).then(l=>{l.ok?(Wl({message:"Successfully cleared offline proxies",type:"success"}),r("refresh")):Wl({message:"Failed to clear offline proxies: "+l.status+" "+l.statusText,type:"warning"})}).catch(l=>{Wl({message:"Failed to clear offline proxies: "+l.message,type:"warning"})})};return(l,u)=>{const f=yg,c=eV,h=q6,d=gW,v=N8,p=pW,m=bte,g=p6;return G(),ce(ft,null,[ee("div",null,[Z(h,{icon:null,style:{width:"100%","margin-left":"30px","margin-bottom":"20px"}},{title:j(()=>[ee("span",null,be(l.proxyType),1)]),content:j(()=>[]),extra:j(()=>[ee("div",Cte,[Z(c,{title:"Are you sure to clear all data of offline proxies?",onConfirm:s},{reference:j(()=>[Z(f,null,{default:j(()=>[mt("ClearOfflineProxies")]),_:1})]),_:1}),Z(f,{onClick:u[0]||(u[0]=y=>l.$emit("refresh"))},{default:j(()=>[mt("Refresh")]),_:1})])]),_:1}),Z(p,{data:l.proxies,"default-sort":{prop:"name",order:"ascending"},style:{width:"100%"}},{default:j(()=>[Z(d,{type:"expand"},{default:j(y=>[Z(xte,{row:y.row,proxyType:l.proxyType},null,8,["row","proxyType"])]),_:1}),Z(d,{label:"Name",prop:"name",sortable:""}),Z(d,{label:"Port",prop:"port",sortable:""}),Z(d,{label:"Connections",prop:"conns",sortable:""}),Z(d,{label:"Traffic In",prop:"trafficIn",formatter:a,sortable:""}),Z(d,{label:"Traffic Out",prop:"trafficOut",formatter:o,sortable:""}),Z(d,{label:"ClientVersion",prop:"clientVersion",sortable:""}),Z(d,{label:"Status",prop:"status",sortable:""},{default:j(y=>[y.row.status==="online"?(G(),de(v,{key:0,type:"success"},{default:j(()=>[mt(be(y.row.status),1)]),_:2},1024)):(G(),de(v,{key:1,type:"danger"},{default:j(()=>[mt(be(y.row.status),1)]),_:2},1024))]),_:1}),Z(d,{label:"Operations"},{default:j(y=>[Z(f,{type:"primary",name:y.row.name,style:{"margin-bottom":"10px"},onClick:_=>{i.value=y.row.name,n.value=!0}},{default:j(()=>[mt("Traffic ")]),_:2},1032,["name","onClick"])]),_:1})]),_:1},8,["data"])]),Z(g,{modelValue:n.value,"onUpdate:modelValue":u[1]||(u[1]=y=>n.value=y),"destroy-on-close":"true",title:i.value,width:"700px"},{default:j(()=>[Z(m,{proxyName:i.value},null,8,["proxyName"])]),_:1},8,["modelValue","title"])],64)}}}),Tte=ie({__name:"ProxiesTCP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/tcp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new hte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"tcp",onRefresh:r},null,8,["proxies"]))}}),Mte=ie({__name:"ProxiesUDP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/udp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new vte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"udp",onRefresh:r},null,8,["proxies"]))}}),Ate=ie({__name:"ProxiesHTTP",setup(e){let t=$([]);const r=()=>{let n,i;fetch("../api/serverinfo",{credentials:"include"}).then(a=>a.json()).then(a=>{n=a.vhostHTTPPort,i=a.subdomainHost,!(n==null||n==0)&&fetch("../api/proxy/http",{credentials:"include"}).then(o=>o.json()).then(o=>{t.value=[];for(let s of o.proxies)t.value.push(new pte(s,n,i))})})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"http",onRefresh:r},null,8,["proxies"]))}}),Pte=ie({__name:"ProxiesHTTPS",setup(e){let t=$([]);const r=()=>{let n,i;fetch("../api/serverinfo",{credentials:"include"}).then(a=>a.json()).then(a=>{n=a.vhostHTTPSPort,i=a.subdomainHost,!(n==null||n==0)&&fetch("../api/proxy/https",{credentials:"include"}).then(o=>o.json()).then(o=>{t.value=[];for(let s of o.proxies)t.value.push(new gte(s,n,i))})})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"https",onRefresh:r},null,8,["proxies"]))}}),Ete=ie({__name:"ProxiesSTCP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/stcp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new mte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"stcp",onRefresh:r},null,8,["proxies"]))}}),Lte=ie({__name:"ProxiesSUDP",setup(e){let t=$([]);const r=()=>{fetch("../api/proxy/sudp",{credentials:"include"}).then(n=>n.json()).then(n=>{t.value=[];for(let i of n.proxies)t.value.push(new yte(i))})};return r(),(n,i)=>(G(),de(Ks,{proxies:T(t),proxyType:"sudp",onRefresh:r},null,8,["proxies"]))}}),Dte=jG({history:uG(),routes:[{path:"/",name:"ServerOverview",component:dte},{path:"/proxies/tcp",name:"ProxiesTCP",component:Tte},{path:"/proxies/udp",name:"ProxiesUDP",component:Mte},{path:"/proxies/http",name:"ProxiesHTTP",component:Ate},{path:"/proxies/https",name:"ProxiesHTTPS",component:Pte},{path:"/proxies/stcp",name:"ProxiesSTCP",component:Ete},{path:"/proxies/sudp",name:"ProxiesSUDP",component:Lte}]}),MD=oR(GW);MD.use(Dte);MD.mount("#app")});export default Ite();
diff --git a/assets/frps/static/index.html b/assets/frps/static/index.html
index 53028f2326e..0a1966e8306 100644
--- a/assets/frps/static/index.html
+++ b/assets/frps/static/index.html
@@ -4,7 +4,7 @@
frps dashboard
-
+
diff --git a/client/admin_api.go b/client/admin_api.go
index e15fc4a4da5..ff889d52355 100644
--- a/client/admin_api.go
+++ b/client/admin_api.go
@@ -76,9 +76,9 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
strictConfigMode, _ = strconv.ParseBool(strictStr)
}
- log.Info("api request [/api/reload]")
+ log.Infof("api request [/api/reload]")
defer func() {
- log.Info("api response [/api/reload], code [%d]", res.Code)
+ log.Infof("api response [/api/reload], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
@@ -89,32 +89,32 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
if err != nil {
res.Code = 400
res.Msg = err.Error()
- log.Warn("reload frpc proxy config error: %s", res.Msg)
+ log.Warnf("reload frpc proxy config error: %s", res.Msg)
return
}
if _, err := validation.ValidateAllClientConfig(cliCfg, proxyCfgs, visitorCfgs); err != nil {
res.Code = 400
res.Msg = err.Error()
- log.Warn("reload frpc proxy config error: %s", res.Msg)
+ log.Warnf("reload frpc proxy config error: %s", res.Msg)
return
}
if err := svr.UpdateAllConfigurer(proxyCfgs, visitorCfgs); err != nil {
res.Code = 500
res.Msg = err.Error()
- log.Warn("reload frpc proxy config error: %s", res.Msg)
+ log.Warnf("reload frpc proxy config error: %s", res.Msg)
return
}
- log.Info("success reload conf")
+ log.Infof("success reload conf")
}
// POST /api/stop
func (svr *Service) apiStop(w http.ResponseWriter, _ *http.Request) {
res := GeneralResponse{Code: 200}
- log.Info("api request [/api/stop]")
+ log.Infof("api request [/api/stop]")
defer func() {
- log.Info("api response [/api/stop], code [%d]", res.Code)
+ log.Infof("api response [/api/stop], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
@@ -165,9 +165,9 @@ func (svr *Service) apiStatus(w http.ResponseWriter, _ *http.Request) {
res StatusResp = make(map[string][]ProxyStatusResp)
)
- log.Info("Http request [/api/status]")
+ log.Infof("Http request [/api/status]")
defer func() {
- log.Info("Http response [/api/status]")
+ log.Infof("Http response [/api/status]")
buf, _ = json.Marshal(&res)
_, _ = w.Write(buf)
}()
@@ -198,9 +198,9 @@ func (svr *Service) apiStatus(w http.ResponseWriter, _ *http.Request) {
func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
res := GeneralResponse{Code: 200}
- log.Info("Http get request [/api/config]")
+ log.Infof("Http get request [/api/config]")
defer func() {
- log.Info("Http get response [/api/config], code [%d]", res.Code)
+ log.Infof("Http get response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
@@ -210,7 +210,7 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
if svr.configFilePath == "" {
res.Code = 400
res.Msg = "frpc has no config file path"
- log.Warn("%s", res.Msg)
+ log.Warnf("%s", res.Msg)
return
}
@@ -218,7 +218,7 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
if err != nil {
res.Code = 400
res.Msg = err.Error()
- log.Warn("load frpc config file error: %s", res.Msg)
+ log.Warnf("load frpc config file error: %s", res.Msg)
return
}
res.Msg = string(content)
@@ -228,9 +228,9 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
- log.Info("Http put request [/api/config]")
+ log.Infof("Http put request [/api/config]")
defer func() {
- log.Info("Http put response [/api/config], code [%d]", res.Code)
+ log.Infof("Http put response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
@@ -242,21 +242,21 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
if err != nil {
res.Code = 400
res.Msg = fmt.Sprintf("read request body error: %v", err)
- log.Warn("%s", res.Msg)
+ log.Warnf("%s", res.Msg)
return
}
if len(body) == 0 {
res.Code = 400
res.Msg = "body can't be empty"
- log.Warn("%s", res.Msg)
+ log.Warnf("%s", res.Msg)
return
}
if err := os.WriteFile(svr.configFilePath, body, 0o644); err != nil {
res.Code = 500
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
- log.Warn("%s", res.Msg)
+ log.Warnf("%s", res.Msg)
return
}
}
diff --git a/client/connector.go b/client/connector.go
index 8c3cd1ec68d..184194a171f 100644
--- a/client/connector.go
+++ b/client/connector.go
@@ -84,7 +84,7 @@ func (c *defaultConnectorImpl) Open() error {
tlsConfig, err = transport.NewClientTLSConfig("", "", "", sn)
}
if err != nil {
- xl.Warn("fail to build tls configuration, err: %v", err)
+ xl.Warnf("fail to build tls configuration, err: %v", err)
return err
}
tlsConfig.NextProtos = []string{"frp"}
@@ -164,14 +164,14 @@ func (c *defaultConnectorImpl) realConnect() (net.Conn, error) {
c.cfg.Transport.TLS.TrustedCaFile,
sn)
if err != nil {
- xl.Warn("fail to build tls configuration, err: %v", err)
+ xl.Warnf("fail to build tls configuration, err: %v", err)
return nil, err
}
}
proxyType, addr, auth, err := libdial.ParseProxyURL(c.cfg.Transport.ProxyURL)
if err != nil {
- xl.Error("fail to parse proxy url")
+ xl.Errorf("fail to parse proxy url")
return nil, err
}
dialOptions := []libdial.DialOption{}
diff --git a/client/control.go b/client/control.go
index b272ee06800..e1916890573 100644
--- a/client/control.go
+++ b/client/control.go
@@ -124,7 +124,7 @@ func (ctl *Control) handleReqWorkConn(_ msg.Message) {
xl := ctl.xl
workConn, err := ctl.connectServer()
if err != nil {
- xl.Warn("start new connection to server error: %v", err)
+ xl.Warnf("start new connection to server error: %v", err)
return
}
@@ -132,24 +132,24 @@ func (ctl *Control) handleReqWorkConn(_ msg.Message) {
RunID: ctl.sessionCtx.RunID,
}
if err = ctl.sessionCtx.AuthSetter.SetNewWorkConn(m); err != nil {
- xl.Warn("error during NewWorkConn authentication: %v", err)
+ xl.Warnf("error during NewWorkConn authentication: %v", err)
workConn.Close()
return
}
if err = msg.WriteMsg(workConn, m); err != nil {
- xl.Warn("work connection write to server error: %v", err)
+ xl.Warnf("work connection write to server error: %v", err)
workConn.Close()
return
}
var startMsg msg.StartWorkConn
if err = msg.ReadMsgInto(workConn, &startMsg); err != nil {
- xl.Trace("work connection closed before response StartWorkConn message: %v", err)
+ xl.Tracef("work connection closed before response StartWorkConn message: %v", err)
workConn.Close()
return
}
if startMsg.Error != "" {
- xl.Error("StartWorkConn contains error: %s", startMsg.Error)
+ xl.Errorf("StartWorkConn contains error: %s", startMsg.Error)
workConn.Close()
return
}
@@ -165,9 +165,9 @@ func (ctl *Control) handleNewProxyResp(m msg.Message) {
// Start a new proxy handler if no error got
err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
if err != nil {
- xl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
+ xl.Warnf("[%s] start error: %v", inMsg.ProxyName, err)
} else {
- xl.Info("[%s] start proxy success", inMsg.ProxyName)
+ xl.Infof("[%s] start proxy success", inMsg.ProxyName)
}
}
@@ -178,7 +178,7 @@ func (ctl *Control) handleNatHoleResp(m msg.Message) {
// Dispatch the NatHoleResp message to the related proxy.
ok := ctl.msgTransporter.DispatchWithType(inMsg, msg.TypeNameNatHoleResp, inMsg.TransactionID)
if !ok {
- xl.Trace("dispatch NatHoleResp message to related proxy error")
+ xl.Tracef("dispatch NatHoleResp message to related proxy error")
}
}
@@ -187,12 +187,12 @@ func (ctl *Control) handlePong(m msg.Message) {
inMsg := m.(*msg.Pong)
if inMsg.Error != "" {
- xl.Error("Pong message contains error: %s", inMsg.Error)
+ xl.Errorf("Pong message contains error: %s", inMsg.Error)
ctl.closeSession()
return
}
ctl.lastPong.Store(time.Now())
- xl.Debug("receive heartbeat from server")
+ xl.Debugf("receive heartbeat from server")
}
// closeSession closes the control connection.
@@ -241,10 +241,10 @@ func (ctl *Control) heartbeatWorker() {
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 {
// send heartbeat to server
sendHeartBeat := func() (bool, error) {
- xl.Debug("send heartbeat to server")
+ xl.Debugf("send heartbeat to server")
pingMsg := &msg.Ping{}
if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil {
- xl.Warn("error during ping authentication: %v, skip sending ping message", err)
+ xl.Warnf("error during ping authentication: %v, skip sending ping message", err)
return false, err
}
_ = ctl.msgDispatcher.Send(pingMsg)
@@ -269,7 +269,7 @@ func (ctl *Control) heartbeatWorker() {
go wait.Until(func() {
if time.Since(ctl.lastPong.Load().(time.Time)) > time.Duration(ctl.sessionCtx.Common.Transport.HeartbeatTimeout)*time.Second {
- xl.Warn("heartbeat timeout")
+ xl.Warnf("heartbeat timeout")
ctl.closeSession()
return
}
diff --git a/client/health/health.go b/client/health/health.go
index 63eec721811..d298e61520d 100644
--- a/client/health/health.go
+++ b/client/health/health.go
@@ -40,8 +40,8 @@ type Monitor struct {
addr string
// For http
- url string
-
+ url string
+ header http.Header
failedTimes uint64
statusOK bool
statusNormalFn func()
@@ -73,6 +73,11 @@ func NewMonitor(ctx context.Context, cfg v1.HealthCheckConfig, addr string,
}
url = s + cfg.Path
}
+ header := make(http.Header)
+ for _, h := range cfg.HTTPHeaders {
+ header.Set(h.Name, h.Value)
+ }
+
return &Monitor{
checkType: cfg.Type,
interval: time.Duration(cfg.IntervalSeconds) * time.Second,
@@ -80,6 +85,7 @@ func NewMonitor(ctx context.Context, cfg v1.HealthCheckConfig, addr string,
maxFailedTimes: cfg.MaxFailed,
addr: addr,
url: url,
+ header: header,
statusOK: false,
statusNormalFn: statusNormalFn,
statusFailedFn: statusFailedFn,
@@ -112,17 +118,17 @@ func (monitor *Monitor) checkWorker() {
}
if err == nil {
- xl.Trace("do one health check success")
+ xl.Tracef("do one health check success")
if !monitor.statusOK && monitor.statusNormalFn != nil {
- xl.Info("health check status change to success")
+ xl.Infof("health check status change to success")
monitor.statusOK = true
monitor.statusNormalFn()
}
} else {
- xl.Warn("do one health check failed: %v", err)
+ xl.Warnf("do one health check failed: %v", err)
monitor.failedTimes++
if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
- xl.Warn("health check status change to failed")
+ xl.Warnf("health check status change to failed")
monitor.statusOK = false
monitor.statusFailedFn()
}
@@ -163,6 +169,8 @@ func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
if err != nil {
return err
}
+ req.Header = monitor.header
+ req.Host = monitor.header.Get("Host")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go
index 396539c0837..16295e052c7 100644
--- a/client/proxy/proxy.go
+++ b/client/proxy/proxy.go
@@ -141,13 +141,13 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
})
}
- xl.Trace("handle tcp work connection, useEncryption: %t, useCompression: %t",
+ xl.Tracef("handle tcp work connection, useEncryption: %t, useCompression: %t",
baseCfg.Transport.UseEncryption, baseCfg.Transport.UseCompression)
if baseCfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, encKey)
if err != nil {
workConn.Close()
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
@@ -158,40 +158,42 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
// check if we need to send proxy protocol info
var extraInfo plugin.ExtraInfo
- if baseCfg.Transport.ProxyProtocolVersion != "" {
- if m.SrcAddr != "" && m.SrcPort != 0 {
- if m.DstAddr == "" {
- m.DstAddr = "127.0.0.1"
- }
- srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
- dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
- h := &pp.Header{
- Command: pp.PROXY,
- SourceAddr: srcAddr,
- DestinationAddr: dstAddr,
- }
-
- if strings.Contains(m.SrcAddr, ".") {
- h.TransportProtocol = pp.TCPv4
- } else {
- h.TransportProtocol = pp.TCPv6
- }
-
- if baseCfg.Transport.ProxyProtocolVersion == "v1" {
- h.Version = 1
- } else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
- h.Version = 2
- }
-
- extraInfo.ProxyProtocolHeader = h
+ if m.SrcAddr != "" && m.SrcPort != 0 {
+ if m.DstAddr == "" {
+ m.DstAddr = "127.0.0.1"
}
+ srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
+ dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
+ extraInfo.SrcAddr = srcAddr
+ extraInfo.DstAddr = dstAddr
+ }
+
+ if baseCfg.Transport.ProxyProtocolVersion != "" && m.SrcAddr != "" && m.SrcPort != 0 {
+ h := &pp.Header{
+ Command: pp.PROXY,
+ SourceAddr: extraInfo.SrcAddr,
+ DestinationAddr: extraInfo.DstAddr,
+ }
+
+ if strings.Contains(m.SrcAddr, ".") {
+ h.TransportProtocol = pp.TCPv4
+ } else {
+ h.TransportProtocol = pp.TCPv6
+ }
+
+ if baseCfg.Transport.ProxyProtocolVersion == "v1" {
+ h.Version = 1
+ } else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
+ h.Version = 2
+ }
+ extraInfo.ProxyProtocolHeader = h
}
if pxy.proxyPlugin != nil {
// if plugin is set, let plugin handle connection first
- xl.Debug("handle by plugin: %s", pxy.proxyPlugin.Name())
+ xl.Debugf("handle by plugin: %s", pxy.proxyPlugin.Name())
pxy.proxyPlugin.Handle(remote, workConn, &extraInfo)
- xl.Debug("handle by plugin finished")
+ xl.Debugf("handle by plugin finished")
return
}
@@ -201,25 +203,25 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
)
if err != nil {
workConn.Close()
- xl.Error("connect to local service [%s:%d] error: %v", baseCfg.LocalIP, baseCfg.LocalPort, err)
+ xl.Errorf("connect to local service [%s:%d] error: %v", baseCfg.LocalIP, baseCfg.LocalPort, err)
return
}
- xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
+ xl.Debugf("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
if extraInfo.ProxyProtocolHeader != nil {
if _, err := extraInfo.ProxyProtocolHeader.WriteTo(localConn); err != nil {
workConn.Close()
- xl.Error("write proxy protocol header to local conn error: %v", err)
+ xl.Errorf("write proxy protocol header to local conn error: %v", err)
return
}
}
_, _, errs := libio.Join(localConn, remote)
- xl.Debug("join connections closed")
+ xl.Debugf("join connections closed")
if len(errs) > 0 {
- xl.Trace("join connections errors: %v", errs)
+ xl.Tracef("join connections errors: %v", errs)
}
if compressionResourceRecycleFn != nil {
compressionResourceRecycleFn()
diff --git a/client/proxy/proxy_manager.go b/client/proxy/proxy_manager.go
index 12e2f6cfee8..95778ce0213 100644
--- a/client/proxy/proxy_manager.go
+++ b/client/proxy/proxy_manager.go
@@ -152,7 +152,7 @@ func (pm *Manager) UpdateAll(proxyCfgs []v1.ProxyConfigurer) {
}
}
if len(delPxyNames) > 0 {
- xl.Info("proxy removed: %s", delPxyNames)
+ xl.Infof("proxy removed: %s", delPxyNames)
}
addPxyNames := make([]string, 0)
@@ -170,6 +170,6 @@ func (pm *Manager) UpdateAll(proxyCfgs []v1.ProxyConfigurer) {
}
}
if len(addPxyNames) > 0 {
- xl.Info("proxy added: %s", addPxyNames)
+ xl.Infof("proxy added: %s", addPxyNames)
}
}
diff --git a/client/proxy/proxy_wrapper.go b/client/proxy/proxy_wrapper.go
index 84f24abb668..487e3702b9e 100644
--- a/client/proxy/proxy_wrapper.go
+++ b/client/proxy/proxy_wrapper.go
@@ -114,7 +114,7 @@ func NewWrapper(
addr := net.JoinHostPort(baseInfo.LocalIP, strconv.Itoa(baseInfo.LocalPort))
pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheck, addr,
pw.statusNormalCallback, pw.statusFailedCallback)
- xl.Trace("enable health check monitor")
+ xl.Tracef("enable health check monitor")
}
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, pw.msgTransporter)
@@ -197,7 +197,7 @@ func (pw *Wrapper) checkWorker() {
(pw.Phase == ProxyPhaseWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
(pw.Phase == ProxyPhaseStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
- xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseWaitStart)
+ xl.Tracef("change status from [%s] to [%s]", pw.Phase, ProxyPhaseWaitStart)
pw.Phase = ProxyPhaseWaitStart
var newProxyMsg msg.NewProxy
@@ -212,7 +212,7 @@ func (pw *Wrapper) checkWorker() {
pw.mu.Lock()
if pw.Phase == ProxyPhaseRunning || pw.Phase == ProxyPhaseWaitStart {
pw.close()
- xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseCheckFailed)
+ xl.Tracef("change status from [%s] to [%s]", pw.Phase, ProxyPhaseCheckFailed)
pw.Phase = ProxyPhaseCheckFailed
}
pw.mu.Unlock()
@@ -236,7 +236,7 @@ func (pw *Wrapper) statusNormalCallback() {
default:
}
})
- xl.Info("health check success")
+ xl.Infof("health check success")
}
func (pw *Wrapper) statusFailedCallback() {
@@ -248,7 +248,7 @@ func (pw *Wrapper) statusFailedCallback() {
default:
}
})
- xl.Info("health check failed")
+ xl.Infof("health check failed")
}
func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
@@ -257,7 +257,7 @@ func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
pxy := pw.pxy
pw.mu.RUnlock()
if pxy != nil && pw.Phase == ProxyPhaseRunning {
- xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
+ xl.Debugf("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
go pxy.InWorkConn(workConn, m)
} else {
workConn.Close()
diff --git a/client/proxy/sudp.go b/client/proxy/sudp.go
index 4d06170dc90..ad9db89a940 100644
--- a/client/proxy/sudp.go
+++ b/client/proxy/sudp.go
@@ -81,7 +81,7 @@ func (pxy *SUDPProxy) Close() {
func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
xl := pxy.xl
- xl.Info("incoming a new work connection for sudp proxy, %s", conn.RemoteAddr().String())
+ xl.Infof("incoming a new work connection for sudp proxy, %s", conn.RemoteAddr().String())
var rwc io.ReadWriteCloser = conn
var err error
@@ -94,7 +94,7 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Auth.Token))
if err != nil {
conn.Close()
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
@@ -133,21 +133,21 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
// first to check sudp proxy is closed or not
select {
case <-pxy.closeCh:
- xl.Trace("frpc sudp proxy is closed")
+ xl.Tracef("frpc sudp proxy is closed")
return
default:
}
var udpMsg msg.UDPPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
- xl.Warn("read from workConn for sudp error: %v", errRet)
+ xl.Warnf("read from workConn for sudp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
readCh <- &udpMsg
}); errRet != nil {
- xl.Warn("reader goroutine for sudp work connection closed: %v", errRet)
+ xl.Warnf("reader goroutine for sudp work connection closed: %v", errRet)
return
}
}
@@ -157,21 +157,21 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
closeFn()
- xl.Info("writer goroutine for sudp work connection closed")
+ xl.Infof("writer goroutine for sudp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UDPPacket:
- xl.Trace("frpc send udp package to frpc visitor, [udp local: %v, remote: %v], [tcp work conn local: %v, remote: %v]",
+ xl.Tracef("frpc send udp package to frpc visitor, [udp local: %v, remote: %v], [tcp work conn local: %v, remote: %v]",
m.LocalAddr.String(), m.RemoteAddr.String(), conn.LocalAddr().String(), conn.RemoteAddr().String())
case *msg.Ping:
- xl.Trace("frpc send ping message to frpc visitor")
+ xl.Tracef("frpc send ping message to frpc visitor")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
- xl.Error("sudp work write error: %v", errRet)
+ xl.Errorf("sudp work write error: %v", errRet)
return
}
}
@@ -191,11 +191,11 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
- xl.Warn("heartbeat goroutine for sudp work connection closed")
+ xl.Warnf("heartbeat goroutine for sudp work connection closed")
return
}
case <-pxy.closeCh:
- xl.Trace("frpc sudp proxy is closed")
+ xl.Tracef("frpc sudp proxy is closed")
return
}
}
diff --git a/client/proxy/udp.go b/client/proxy/udp.go
index 38d14ff598b..b08fe1608ad 100644
--- a/client/proxy/udp.go
+++ b/client/proxy/udp.go
@@ -90,7 +90,7 @@ func (pxy *UDPProxy) Close() {
func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
xl := pxy.xl
- xl.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
+ xl.Infof("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
// close resources related with old workConn
pxy.Close()
@@ -105,7 +105,7 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Auth.Token))
if err != nil {
conn.Close()
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
@@ -125,32 +125,32 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
for {
var udpMsg msg.UDPPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
- xl.Warn("read from workConn for udp error: %v", errRet)
+ xl.Warnf("read from workConn for udp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
- xl.Trace("get udp package from workConn: %s", udpMsg.Content)
+ xl.Tracef("get udp package from workConn: %s", udpMsg.Content)
readCh <- &udpMsg
}); errRet != nil {
- xl.Info("reader goroutine for udp work connection closed: %v", errRet)
+ xl.Infof("reader goroutine for udp work connection closed: %v", errRet)
return
}
}
}
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
- xl.Info("writer goroutine for udp work connection closed")
+ xl.Infof("writer goroutine for udp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UDPPacket:
- xl.Trace("send udp package to workConn: %s", m.Content)
+ xl.Tracef("send udp package to workConn: %s", m.Content)
case *msg.Ping:
- xl.Trace("send ping message to udp workConn")
+ xl.Tracef("send ping message to udp workConn")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
- xl.Error("udp work write error: %v", errRet)
+ xl.Errorf("udp work write error: %v", errRet)
return
}
}
@@ -162,7 +162,7 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
- xl.Trace("heartbeat goroutine for udp work connection closed")
+ xl.Tracef("heartbeat goroutine for udp work connection closed")
break
}
}
diff --git a/client/proxy/xtcp.go b/client/proxy/xtcp.go
index e5e5d47e280..31f9ac89734 100644
--- a/client/proxy/xtcp.go
+++ b/client/proxy/xtcp.go
@@ -59,17 +59,17 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
var natHoleSidMsg msg.NatHoleSid
err := msg.ReadMsgInto(conn, &natHoleSidMsg)
if err != nil {
- xl.Error("xtcp read from workConn error: %v", err)
+ xl.Errorf("xtcp read from workConn error: %v", err)
return
}
- xl.Trace("nathole prepare start")
+ xl.Tracef("nathole prepare start")
prepareResult, err := nathole.Prepare([]string{pxy.clientCfg.NatHoleSTUNServer})
if err != nil {
- xl.Warn("nathole prepare error: %v", err)
+ xl.Warnf("nathole prepare error: %v", err)
return
}
- xl.Info("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
+ xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
defer prepareResult.ListenConn.Close()
@@ -83,14 +83,14 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
AssistedAddrs: prepareResult.AssistedAddrs,
}
- xl.Trace("nathole exchange info start")
+ xl.Tracef("nathole exchange info start")
natHoleRespMsg, err := nathole.ExchangeInfo(pxy.ctx, pxy.msgTransporter, transactionID, natHoleClientMsg, 5*time.Second)
if err != nil {
- xl.Warn("nathole exchange info error: %v", err)
+ xl.Warnf("nathole exchange info error: %v", err)
return
}
- xl.Info("get natHoleRespMsg, sid [%s], protocol [%s], candidate address %v, assisted address %v, detectBehavior: %+v",
+ xl.Infof("get natHoleRespMsg, sid [%s], protocol [%s], candidate address %v, assisted address %v, detectBehavior: %+v",
natHoleRespMsg.Sid, natHoleRespMsg.Protocol, natHoleRespMsg.CandidateAddrs,
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
@@ -98,7 +98,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
newListenConn, raddr, err := nathole.MakeHole(pxy.ctx, listenConn, natHoleRespMsg, []byte(pxy.cfg.Secretkey))
if err != nil {
listenConn.Close()
- xl.Warn("make hole error: %v", err)
+ xl.Warnf("make hole error: %v", err)
_ = pxy.msgTransporter.Send(&msg.NatHoleReport{
Sid: natHoleRespMsg.Sid,
Success: false,
@@ -106,7 +106,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
return
}
listenConn = newListenConn
- xl.Info("establishing nat hole connection successful, sid [%s], remoteAddr [%s]", natHoleRespMsg.Sid, raddr)
+ xl.Infof("establishing nat hole connection successful, sid [%s], remoteAddr [%s]", natHoleRespMsg.Sid, raddr)
_ = pxy.msgTransporter.Send(&msg.NatHoleReport{
Sid: natHoleRespMsg.Sid,
@@ -128,14 +128,14 @@ func (pxy *XTCPProxy) listenByKCP(listenConn *net.UDPConn, raddr *net.UDPAddr, s
laddr, _ := net.ResolveUDPAddr("udp", listenConn.LocalAddr().String())
lConn, err := net.DialUDP("udp", laddr, raddr)
if err != nil {
- xl.Warn("dial udp error: %v", err)
+ xl.Warnf("dial udp error: %v", err)
return
}
defer lConn.Close()
remote, err := netpkg.NewKCPConnFromUDP(lConn, true, raddr.String())
if err != nil {
- xl.Warn("create kcp connection from udp connection error: %v", err)
+ xl.Warnf("create kcp connection from udp connection error: %v", err)
return
}
@@ -145,7 +145,7 @@ func (pxy *XTCPProxy) listenByKCP(listenConn *net.UDPConn, raddr *net.UDPAddr, s
fmuxCfg.LogOutput = io.Discard
session, err := fmux.Server(remote, fmuxCfg)
if err != nil {
- xl.Error("create mux session error: %v", err)
+ xl.Errorf("create mux session error: %v", err)
return
}
defer session.Close()
@@ -153,7 +153,7 @@ func (pxy *XTCPProxy) listenByKCP(listenConn *net.UDPConn, raddr *net.UDPAddr, s
for {
muxConn, err := session.Accept()
if err != nil {
- xl.Error("accept connection error: %v", err)
+ xl.Errorf("accept connection error: %v", err)
return
}
go pxy.HandleTCPWorkConnection(muxConn, startWorkConnMsg, []byte(pxy.cfg.Secretkey))
@@ -166,7 +166,7 @@ func (pxy *XTCPProxy) listenByQUIC(listenConn *net.UDPConn, _ *net.UDPAddr, star
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
if err != nil {
- xl.Warn("create tls config error: %v", err)
+ xl.Warnf("create tls config error: %v", err)
return
}
tlsConfig.NextProtos = []string{"frp"}
@@ -178,19 +178,19 @@ func (pxy *XTCPProxy) listenByQUIC(listenConn *net.UDPConn, _ *net.UDPAddr, star
},
)
if err != nil {
- xl.Warn("dial quic error: %v", err)
+ xl.Warnf("dial quic error: %v", err)
return
}
// only accept one connection from raddr
c, err := quicListener.Accept(pxy.ctx)
if err != nil {
- xl.Error("quic accept connection error: %v", err)
+ xl.Errorf("quic accept connection error: %v", err)
return
}
for {
stream, err := c.AcceptStream(pxy.ctx)
if err != nil {
- xl.Debug("quic accept stream error: %v", err)
+ xl.Debugf("quic accept stream error: %v", err)
_ = c.CloseWithError(0, "")
return
}
diff --git a/client/service.go b/client/service.go
index 3d1a21b0e04..7a7f6dc88d2 100644
--- a/client/service.go
+++ b/client/service.go
@@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"net"
+ "os"
"runtime"
"sync"
"time"
@@ -40,6 +41,12 @@ import (
func init() {
crypto.DefaultSalt = "frp"
+ // Disable quic-go's receive buffer warning.
+ os.Setenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING", "true")
+ // Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
+ if os.Getenv("QUIC_GO_DISABLE_ECN") == "" {
+ os.Setenv("QUIC_GO_DISABLE_ECN", "true")
+ }
}
type cancelErr struct {
@@ -174,9 +181,9 @@ func (svr *Service) Run(ctx context.Context) error {
if svr.webServer != nil {
go func() {
- log.Info("admin server listen on %s", svr.webServer.Address())
+ log.Infof("admin server listen on %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
- log.Warn("admin server exit with error: %v", err)
+ log.Warnf("admin server exit with error: %v", err)
}
}()
}
@@ -269,14 +276,14 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) {
if loginRespMsg.Error != "" {
err = fmt.Errorf("%s", loginRespMsg.Error)
- xl.Error("%s", loginRespMsg.Error)
+ xl.Errorf("%s", loginRespMsg.Error)
return
}
svr.runID = loginRespMsg.RunID
xl.AddPrefix(xlog.LogPrefix{Name: "runID", Value: svr.runID})
- xl.Info("login to server success, get run id [%s]", loginRespMsg.RunID)
+ xl.Infof("login to server success, get run id [%s]", loginRespMsg.RunID)
return
}
@@ -284,10 +291,10 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
xl := xlog.FromContextSafe(svr.ctx)
loginFunc := func() (bool, error) {
- xl.Info("try to connect to server...")
+ xl.Infof("try to connect to server...")
conn, connector, err := svr.login()
if err != nil {
- xl.Warn("connect to server error: %v", err)
+ xl.Warnf("connect to server error: %v", err)
if firstLoginExit {
svr.cancel(cancelErr{Err: err})
}
@@ -313,7 +320,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
ctl, err := NewControl(svr.ctx, sessionCtx)
if err != nil {
conn.Close()
- xl.Error("NewControl error: %v", err)
+ xl.Errorf("NewControl error: %v", err)
return false, err
}
ctl.SetInWorkConnCallback(svr.handleWorkConnCb)
diff --git a/client/visitor/stcp.go b/client/visitor/stcp.go
index 58433879492..b26faf5200c 100644
--- a/client/visitor/stcp.go
+++ b/client/visitor/stcp.go
@@ -56,7 +56,7 @@ func (sv *STCPVisitor) worker() {
for {
conn, err := sv.l.Accept()
if err != nil {
- xl.Warn("stcp local listener closed")
+ xl.Warnf("stcp local listener closed")
return
}
go sv.handleConn(conn)
@@ -68,7 +68,7 @@ func (sv *STCPVisitor) internalConnWorker() {
for {
conn, err := sv.internalLn.Accept()
if err != nil {
- xl.Warn("stcp internal listener closed")
+ xl.Warnf("stcp internal listener closed")
return
}
go sv.handleConn(conn)
@@ -79,7 +79,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close()
- xl.Debug("get a new stcp user connection")
+ xl.Debugf("get a new stcp user connection")
visitorConn, err := sv.helper.ConnectServer()
if err != nil {
return
@@ -97,7 +97,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
- xl.Warn("send newVisitorConnMsg to server error: %v", err)
+ xl.Warnf("send newVisitorConnMsg to server error: %v", err)
return
}
@@ -105,13 +105,13 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
_ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
- xl.Warn("get newVisitorConnRespMsg error: %v", err)
+ xl.Warnf("get newVisitorConnRespMsg error: %v", err)
return
}
_ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
- xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
+ xl.Warnf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
return
}
@@ -120,7 +120,7 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
if sv.cfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
diff --git a/client/visitor/sudp.go b/client/visitor/sudp.go
index 1d489bec42b..284aee10915 100644
--- a/client/visitor/sudp.go
+++ b/client/visitor/sudp.go
@@ -62,7 +62,7 @@ func (sv *SUDPVisitor) Run() (err error) {
sv.sendCh = make(chan *msg.UDPPacket, 1024)
sv.readCh = make(chan *msg.UDPPacket, 1024)
- xl.Info("sudp start to work, listen on %s", addr)
+ xl.Infof("sudp start to work, listen on %s", addr)
go sv.dispatcher()
go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh, int(sv.clientCfg.UDPPacketSize))
@@ -84,17 +84,17 @@ func (sv *SUDPVisitor) dispatcher() {
select {
case firstPacket = <-sv.sendCh:
if firstPacket == nil {
- xl.Info("frpc sudp visitor proxy is closed")
+ xl.Infof("frpc sudp visitor proxy is closed")
return
}
case <-sv.checkCloseCh:
- xl.Info("frpc sudp visitor proxy is closed")
+ xl.Infof("frpc sudp visitor proxy is closed")
return
}
visitorConn, err = sv.getNewVisitorConn()
if err != nil {
- xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
+ xl.Warnf("newVisitorConn to frps error: %v, try to reconnect", err)
continue
}
@@ -111,7 +111,7 @@ func (sv *SUDPVisitor) dispatcher() {
func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
xl := xlog.FromContextSafe(sv.ctx)
- xl.Debug("starting sudp proxy worker")
+ xl.Debugf("starting sudp proxy worker")
wg := &sync.WaitGroup{}
wg.Add(2)
@@ -134,21 +134,21 @@ func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
// frpc will send heartbeat in workConn to frpc visitor for keeping alive
_ = conn.SetReadDeadline(time.Now().Add(60 * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
- xl.Warn("read from workconn for user udp conn error: %v", errRet)
+ xl.Warnf("read from workconn for user udp conn error: %v", errRet)
return
}
_ = conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) {
case *msg.Ping:
- xl.Debug("frpc visitor get ping message from frpc")
+ xl.Debugf("frpc visitor get ping message from frpc")
continue
case *msg.UDPPacket:
if errRet := errors.PanicToError(func() {
sv.readCh <- m
- xl.Trace("frpc visitor get udp packet from workConn: %s", m.Content)
+ xl.Tracef("frpc visitor get udp packet from workConn: %s", m.Content)
}); errRet != nil {
- xl.Info("reader goroutine for udp work connection closed")
+ xl.Infof("reader goroutine for udp work connection closed")
return
}
}
@@ -165,25 +165,25 @@ func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
var errRet error
if firstPacket != nil {
if errRet = msg.WriteMsg(conn, firstPacket); errRet != nil {
- xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
+ xl.Warnf("sender goroutine for udp work connection closed: %v", errRet)
return
}
- xl.Trace("send udp package to workConn: %s", firstPacket.Content)
+ xl.Tracef("send udp package to workConn: %s", firstPacket.Content)
}
for {
select {
case udpMsg, ok := <-sv.sendCh:
if !ok {
- xl.Info("sender goroutine for udp work connection closed")
+ xl.Infof("sender goroutine for udp work connection closed")
return
}
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
- xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
+ xl.Warnf("sender goroutine for udp work connection closed: %v", errRet)
return
}
- xl.Trace("send udp package to workConn: %s", udpMsg.Content)
+ xl.Tracef("send udp package to workConn: %s", udpMsg.Content)
case <-closeCh:
return
}
@@ -194,7 +194,7 @@ func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
go workConnSenderFn(workConn)
wg.Wait()
- xl.Info("sudp worker is closed")
+ xl.Infof("sudp worker is closed")
}
func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
@@ -235,7 +235,7 @@ func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
if sv.cfg.Transport.UseEncryption {
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return nil, err
}
}
diff --git a/client/visitor/visitor_manager.go b/client/visitor/visitor_manager.go
index 4f31f2706ed..6ff65dabae0 100644
--- a/client/visitor/visitor_manager.go
+++ b/client/visitor/visitor_manager.go
@@ -79,14 +79,14 @@ func (vm *Manager) keepVisitorsRunning() {
for {
select {
case <-vm.stopCh:
- xl.Trace("gracefully shutdown visitor manager")
+ xl.Tracef("gracefully shutdown visitor manager")
return
case <-ticker.C:
vm.mu.Lock()
for _, cfg := range vm.cfgs {
name := cfg.GetBaseConfig().Name
if _, exist := vm.visitors[name]; !exist {
- xl.Info("try to start visitor [%s]", name)
+ xl.Infof("try to start visitor [%s]", name)
_ = vm.startVisitor(cfg)
}
}
@@ -115,10 +115,10 @@ func (vm *Manager) startVisitor(cfg v1.VisitorConfigurer) (err error) {
visitor := NewVisitor(vm.ctx, cfg, vm.clientCfg, vm.helper)
err = visitor.Run()
if err != nil {
- xl.Warn("start error: %v", err)
+ xl.Warnf("start error: %v", err)
} else {
vm.visitors[name] = visitor
- xl.Info("start visitor success")
+ xl.Infof("start visitor success")
}
return
}
@@ -156,7 +156,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) {
}
}
if len(delNames) > 0 {
- xl.Info("visitor removed: %v", delNames)
+ xl.Infof("visitor removed: %v", delNames)
}
addNames := make([]string, 0)
@@ -169,7 +169,7 @@ func (vm *Manager) UpdateAll(cfgs []v1.VisitorConfigurer) {
}
}
if len(addNames) > 0 {
- xl.Info("visitor added: %v", addNames)
+ xl.Infof("visitor added: %v", addNames)
}
}
diff --git a/client/visitor/xtcp.go b/client/visitor/xtcp.go
index ad773503e03..a1efd72b62b 100644
--- a/client/visitor/xtcp.go
+++ b/client/visitor/xtcp.go
@@ -93,7 +93,7 @@ func (sv *XTCPVisitor) worker() {
for {
conn, err := sv.l.Accept()
if err != nil {
- xl.Warn("xtcp local listener closed")
+ xl.Warnf("xtcp local listener closed")
return
}
go sv.handleConn(conn)
@@ -105,7 +105,7 @@ func (sv *XTCPVisitor) internalConnWorker() {
for {
conn, err := sv.internalLn.Accept()
if err != nil {
- xl.Warn("xtcp internal listener closed")
+ xl.Warnf("xtcp internal listener closed")
return
}
go sv.handleConn(conn)
@@ -140,14 +140,14 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
case <-sv.ctx.Done():
return
case <-ticker.C:
- xl.Debug("keepTunnelOpenWorker try to check tunnel...")
+ xl.Debugf("keepTunnelOpenWorker try to check tunnel...")
conn, err := sv.getTunnelConn()
if err != nil {
- xl.Warn("keepTunnelOpenWorker get tunnel connection error: %v", err)
+ xl.Warnf("keepTunnelOpenWorker get tunnel connection error: %v", err)
_ = sv.retryLimiter.Wait(sv.ctx)
continue
}
- xl.Debug("keepTunnelOpenWorker check success")
+ xl.Debugf("keepTunnelOpenWorker check success")
if conn != nil {
conn.Close()
}
@@ -164,7 +164,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
}
}()
- xl.Debug("get a new xtcp user connection")
+ xl.Debugf("get a new xtcp user connection")
// Open a tunnel connection to the server. If there is already a successful hole-punching connection,
// it will be reused. Otherwise, it will block and wait for a successful hole-punching connection until timeout.
@@ -176,15 +176,15 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
}
tunnelConn, err := sv.openTunnel(ctx)
if err != nil {
- xl.Error("open tunnel error: %v", err)
+ xl.Errorf("open tunnel error: %v", err)
// no fallback, just return
if sv.cfg.FallbackTo == "" {
return
}
- xl.Debug("try to transfer connection to visitor: %s", sv.cfg.FallbackTo)
+ xl.Debugf("try to transfer connection to visitor: %s", sv.cfg.FallbackTo)
if err := sv.helper.TransferConn(sv.cfg.FallbackTo, userConn); err != nil {
- xl.Error("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
+ xl.Errorf("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
return
}
isConnTrasfered = true
@@ -195,7 +195,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
if sv.cfg.Transport.UseEncryption {
muxConnRWCloser, err = libio.WithEncryption(muxConnRWCloser, []byte(sv.cfg.SecretKey))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
@@ -206,9 +206,9 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
}
_, _, errs := libio.Join(userConn, muxConnRWCloser)
- xl.Debug("join connections closed")
+ xl.Debugf("join connections closed")
if len(errs) > 0 {
- xl.Trace("join connections errors: %v", errs)
+ xl.Tracef("join connections errors: %v", errs)
}
}
@@ -239,7 +239,7 @@ func (sv *XTCPVisitor) openTunnel(ctx context.Context) (conn net.Conn, err error
if err != nil {
if err != ErrNoTunnelSession {
- xl.Warn("get tunnel connection error: %v", err)
+ xl.Warnf("get tunnel connection error: %v", err)
}
continue
}
@@ -268,19 +268,19 @@ func (sv *XTCPVisitor) getTunnelConn() (net.Conn, error) {
// 4. Create a tunnel session using an underlying UDP connection.
func (sv *XTCPVisitor) makeNatHole() {
xl := xlog.FromContextSafe(sv.ctx)
- xl.Trace("makeNatHole start")
+ xl.Tracef("makeNatHole start")
if err := nathole.PreCheck(sv.ctx, sv.helper.MsgTransporter(), sv.cfg.ServerName, 5*time.Second); err != nil {
- xl.Warn("nathole precheck error: %v", err)
+ xl.Warnf("nathole precheck error: %v", err)
return
}
- xl.Trace("nathole prepare start")
+ xl.Tracef("nathole prepare start")
prepareResult, err := nathole.Prepare([]string{sv.clientCfg.NatHoleSTUNServer})
if err != nil {
- xl.Warn("nathole prepare error: %v", err)
+ xl.Warnf("nathole prepare error: %v", err)
return
}
- xl.Info("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
+ xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
listenConn := prepareResult.ListenConn
@@ -298,30 +298,30 @@ func (sv *XTCPVisitor) makeNatHole() {
AssistedAddrs: prepareResult.AssistedAddrs,
}
- xl.Trace("nathole exchange info start")
+ xl.Tracef("nathole exchange info start")
natHoleRespMsg, err := nathole.ExchangeInfo(sv.ctx, sv.helper.MsgTransporter(), transactionID, natHoleVisitorMsg, 5*time.Second)
if err != nil {
listenConn.Close()
- xl.Warn("nathole exchange info error: %v", err)
+ xl.Warnf("nathole exchange info error: %v", err)
return
}
- xl.Info("get natHoleRespMsg, sid [%s], protocol [%s], candidate address %v, assisted address %v, detectBehavior: %+v",
+ xl.Infof("get natHoleRespMsg, sid [%s], protocol [%s], candidate address %v, assisted address %v, detectBehavior: %+v",
natHoleRespMsg.Sid, natHoleRespMsg.Protocol, natHoleRespMsg.CandidateAddrs,
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
newListenConn, raddr, err := nathole.MakeHole(sv.ctx, listenConn, natHoleRespMsg, []byte(sv.cfg.SecretKey))
if err != nil {
listenConn.Close()
- xl.Warn("make hole error: %v", err)
+ xl.Warnf("make hole error: %v", err)
return
}
listenConn = newListenConn
- xl.Info("establishing nat hole connection successful, sid [%s], remoteAddr [%s]", natHoleRespMsg.Sid, raddr)
+ xl.Infof("establishing nat hole connection successful, sid [%s], remoteAddr [%s]", natHoleRespMsg.Sid, raddr)
if err := sv.session.Init(listenConn, raddr); err != nil {
listenConn.Close()
- xl.Warn("init tunnel session error: %v", err)
+ xl.Warnf("init tunnel session error: %v", err)
return
}
}
diff --git a/cmd/frpc/main.go b/cmd/frpc/main.go
index f9a6c252ad1..f7651bca169 100644
--- a/cmd/frpc/main.go
+++ b/cmd/frpc/main.go
@@ -17,8 +17,10 @@ package main
import (
_ "github.com/fatedier/frp/assets/frpc"
"github.com/fatedier/frp/cmd/frpc/sub"
+ "github.com/fatedier/frp/pkg/util/system"
)
func main() {
+ system.EnableCompatibilityMode()
sub.Execute()
}
diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go
index 64cac22fff7..b844ddfd5e9 100644
--- a/cmd/frpc/sub/root.go
+++ b/cmd/frpc/sub/root.go
@@ -46,7 +46,7 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
- rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", false, "strict config parsing mode, unknown fields will cause an error")
+ rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", true, "strict config parsing mode, unknown fields will cause an errors")
}
var rootCmd = &cobra.Command{
@@ -136,11 +136,11 @@ func startService(
visitorCfgs []v1.VisitorConfigurer,
cfgFile string,
) error {
- log.InitLog(cfg.Log.To, cfg.Log.Level, cfg.Log.MaxDays, cfg.Log.DisablePrintColor)
+ log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor)
if cfgFile != "" {
- log.Info("start frpc service for config file [%s]", cfgFile)
- defer log.Info("frpc service for config file [%s] stopped", cfgFile)
+ log.Infof("start frpc service for config file [%s]", cfgFile)
+ defer log.Infof("frpc service for config file [%s] stopped", cfgFile)
}
svr, err := client.NewService(client.ServiceOptions{
Common: cfg,
diff --git a/cmd/frps/main.go b/cmd/frps/main.go
index 34676a2ba6b..3cb398d8290 100644
--- a/cmd/frps/main.go
+++ b/cmd/frps/main.go
@@ -15,13 +15,12 @@
package main
import (
- "github.com/fatedier/golib/crypto"
-
_ "github.com/fatedier/frp/assets/frps"
_ "github.com/fatedier/frp/pkg/metrics"
+ "github.com/fatedier/frp/pkg/util/system"
)
func main() {
- crypto.DefaultSalt = "frp"
+ system.EnableCompatibilityMode()
Execute()
}
diff --git a/cmd/frps/root.go b/cmd/frps/root.go
index cc083518df1..fff487d1074 100644
--- a/cmd/frps/root.go
+++ b/cmd/frps/root.go
@@ -40,7 +40,7 @@ var (
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file of frps")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps")
- rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", false, "strict config parsing mode, unknown fields will cause error")
+ rootCmd.PersistentFlags().BoolVarP(&strictConfigMode, "strict_config", "", true, "strict config parsing mode, unknown fields will cause errors")
config.RegisterServerConfigFlags(rootCmd, &serverCfg)
}
@@ -99,19 +99,19 @@ func Execute() {
}
func runServer(cfg *v1.ServerConfig) (err error) {
- log.InitLog(cfg.Log.To, cfg.Log.Level, cfg.Log.MaxDays, cfg.Log.DisablePrintColor)
+ log.InitLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays), cfg.Log.DisablePrintColor)
if cfgFile != "" {
- log.Info("frps uses config file: %s", cfgFile)
+ log.Infof("frps uses config file: %s", cfgFile)
} else {
- log.Info("frps uses command line arguments for config")
+ log.Infof("frps uses command line arguments for config")
}
svr, err := server.NewService(cfg)
if err != nil {
return err
}
- log.Info("frps started successfully")
+ log.Infof("frps started successfully")
svr.Run(context.Background())
return
}
diff --git a/conf/frpc_full_example.toml b/conf/frpc_full_example.toml
index 59de6fa48e5..0528ddeaf37 100644
--- a/conf/frpc_full_example.toml
+++ b/conf/frpc_full_example.toml
@@ -216,6 +216,10 @@ healthCheck.path = "/status"
healthCheck.intervalSeconds = 10
healthCheck.maxFailed = 3
healthCheck.timeoutSeconds = 3
+# set health check headers
+healthCheck.httpHeaders=[
+ { name = "x-from-where", value = "frp" }
+]
[[proxies]]
name = "web02"
diff --git a/doc/pic/sponsor_daytona.png b/doc/pic/sponsor_daytona.png
new file mode 100644
index 00000000000..b00f9327900
Binary files /dev/null and b/doc/pic/sponsor_daytona.png differ
diff --git a/go.mod b/go.mod
index 49d07fb39c7..d820b96cdd2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,11 @@
module github.com/fatedier/frp
-go 1.21
+go 1.22
require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/coreos/go-oidc/v3 v3.6.0
- github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
- github.com/fatedier/golib v0.3.0
+ github.com/fatedier/golib v0.4.2
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
@@ -25,7 +24,7 @@ require (
github.com/stretchr/testify v1.8.4
github.com/tidwall/gjson v1.17.1
github.com/xtaci/kcp-go/v5 v5.6.7
- golang.org/x/crypto v0.17.0
+ golang.org/x/crypto v0.18.0
golang.org/x/net v0.19.0
golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0
@@ -70,7 +69,7 @@ require (
go.uber.org/mock v0.3.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.11.0 // indirect
- golang.org/x/sys v0.15.0 // indirect
+ golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.9.3 // indirect
google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 2112c7120c7..b00cb78fafd 100644
--- a/go.sum
+++ b/go.sum
@@ -24,10 +24,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
-github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
-github.com/fatedier/golib v0.3.0 h1:xX0QQoYD1ns3IHVDTSysg6gL8buZvGaLyQFvBnWIRF4=
-github.com/fatedier/golib v0.3.0/go.mod h1:pmX4FYyp6N4awxDB6jAgIDVOUWuH+QkVP4v30O+iJDM=
+github.com/fatedier/golib v0.4.2 h1:k+ZBdUFTTipnP1RHfEhGbzyShRdz/rZtFGnjpXG9D9c=
+github.com/fatedier/golib v0.4.2/go.mod h1:gpu+1vXxtJ072NYaNsn/YWgojDL8Ap2kFZQtbzT2qkg=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
@@ -164,8 +162,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
@@ -216,16 +214,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
+golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
diff --git a/package.sh b/package.sh
index de3ab2fded0..8d16f72a6fe 100755
--- a/package.sh
+++ b/package.sh
@@ -1,3 +1,6 @@
+#!/bin/sh
+set -e
+
# compile for version
make
if [ $? -ne 0 ]; then
@@ -14,7 +17,7 @@ make -f ./Makefile.cross-compiles
rm -rf ./release/packages
mkdir -p ./release/packages
-os_all='linux windows darwin freebsd'
+os_all='linux windows darwin freebsd android'
arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64'
cd ./release
diff --git a/pkg/config/load.go b/pkg/config/load.go
index cdbb8e916f4..f9a705eb213 100644
--- a/pkg/config/load.go
+++ b/pkg/config/load.go
@@ -80,7 +80,10 @@ func DetectLegacyINIFormatFromFile(path string) bool {
}
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
- tmpl, err := template.New("frp").Parse(string(in))
+ tmpl, err := template.New("frp").Funcs(template.FuncMap{
+ "parseNumberRange": parseNumberRange,
+ "parseNumberRangePair": parseNumberRangePair,
+ }).Parse(string(in))
if err != nil {
return nil, err
}
diff --git a/pkg/config/template.go b/pkg/config/template.go
new file mode 100644
index 00000000000..44bc456dd7b
--- /dev/null
+++ b/pkg/config/template.go
@@ -0,0 +1,52 @@
+// Copyright 2024 The frp Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "fmt"
+
+ "github.com/fatedier/frp/pkg/util/util"
+)
+
+type NumberPair struct {
+ First int64
+ Second int64
+}
+
+func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, error) {
+ firstRangeNumbers, err := util.ParseRangeNumbers(firstRangeStr)
+ if err != nil {
+ return nil, err
+ }
+ secondRangeNumbers, err := util.ParseRangeNumbers(secondRangeStr)
+ if err != nil {
+ return nil, err
+ }
+ if len(firstRangeNumbers) != len(secondRangeNumbers) {
+ return nil, fmt.Errorf("first and second range numbers are not in pairs")
+ }
+ pairs := make([]NumberPair, 0, len(firstRangeNumbers))
+ for i := 0; i < len(firstRangeNumbers); i++ {
+ pairs = append(pairs, NumberPair{
+ First: firstRangeNumbers[i],
+ Second: secondRangeNumbers[i],
+ })
+ }
+ return pairs, nil
+}
+
+func parseNumberRange(firstRangeStr string) ([]int64, error) {
+ return util.ParseRangeNumbers(firstRangeStr)
+}
diff --git a/pkg/config/types/types.go b/pkg/config/types/types.go
index fac29d74612..a6cd2e71b0e 100644
--- a/pkg/config/types/types.go
+++ b/pkg/config/types/types.go
@@ -45,15 +45,6 @@ func NewBandwidthQuantity(s string) (BandwidthQuantity, error) {
return q, nil
}
-func MustBandwidthQuantity(s string) BandwidthQuantity {
- q := BandwidthQuantity{}
- err := q.UnmarshalString(s)
- if err != nil {
- panic(err)
- }
- return q
-}
-
func (q *BandwidthQuantity) Equal(u *BandwidthQuantity) bool {
if q == nil && u == nil {
return true
diff --git a/pkg/config/v1/common.go b/pkg/config/v1/common.go
index 24ec9b0d825..ddb23356823 100644
--- a/pkg/config/v1/common.go
+++ b/pkg/config/v1/common.go
@@ -129,3 +129,8 @@ type HTTPPluginOptions struct {
type HeaderOperations struct {
Set map[string]string `json:"set,omitempty"`
}
+
+type HTTPHeader struct {
+ Name string `json:"name"`
+ Value string `json:"value"`
+}
diff --git a/pkg/config/v1/plugin.go b/pkg/config/v1/plugin.go
index db9d0d1a0b2..5602a813c3c 100644
--- a/pkg/config/v1/plugin.go
+++ b/pkg/config/v1/plugin.go
@@ -57,7 +57,7 @@ func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
}
if err := decoder.Decode(options); err != nil {
- return err
+ return fmt.Errorf("unmarshal ClientPluginOptions error: %v", err)
}
c.ClientPluginOptions = options
return nil
diff --git a/pkg/config/v1/proxy.go b/pkg/config/v1/proxy.go
index cf87037ac15..1949cfd34d1 100644
--- a/pkg/config/v1/proxy.go
+++ b/pkg/config/v1/proxy.go
@@ -97,6 +97,9 @@ type HealthCheckConfig struct {
// Path specifies the path to send health checks to if the
// health check type is "http".
Path string `json:"path,omitempty"`
+ // HTTPHeaders specifies the headers to send with the health request, if
+ // the health check type is "http".
+ HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty"`
}
type DomainConfig struct {
@@ -186,7 +189,7 @@ func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(configurer); err != nil {
- return err
+ return fmt.Errorf("unmarshal ProxyConfig error: %v", err)
}
c.ProxyConfigurer = configurer
return nil
diff --git a/pkg/config/v1/visitor.go b/pkg/config/v1/visitor.go
index a9b2411ab3d..e9fa166ea57 100644
--- a/pkg/config/v1/visitor.go
+++ b/pkg/config/v1/visitor.go
@@ -114,7 +114,7 @@ func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(configurer); err != nil {
- return err
+ return fmt.Errorf("unmarshal VisitorConfig error: %v", err)
}
c.VisitorConfigurer = configurer
return nil
diff --git a/pkg/metrics/mem/server.go b/pkg/metrics/mem/server.go
index 4f08001b1c4..d92546c4c58 100644
--- a/pkg/metrics/mem/server.go
+++ b/pkg/metrics/mem/server.go
@@ -62,7 +62,7 @@ func (m *serverMetrics) run() {
time.Sleep(12 * time.Hour)
start := time.Now()
count, total := m.clearUselessInfo(time.Duration(7*24) * time.Hour)
- log.Debug("clear useless proxy statistics data count %d/%d, cost %v", count, total, time.Since(start))
+ log.Debugf("clear useless proxy statistics data count %d/%d, cost %v", count, total, time.Since(start))
}
}()
}
@@ -80,7 +80,7 @@ func (m *serverMetrics) clearUselessInfo(continuousOfflineDuration time.Duration
time.Since(data.LastCloseTime) > continuousOfflineDuration {
delete(m.info.ProxyStatistics, name)
count++
- log.Trace("clear proxy [%s]'s statistics data, lastCloseTime: [%s]", name, data.LastCloseTime.String())
+ log.Tracef("clear proxy [%s]'s statistics data, lastCloseTime: [%s]", name, data.LastCloseTime.String())
}
}
return count, total
diff --git a/pkg/msg/ctl.go b/pkg/msg/ctl.go
index 5ccee4a4caf..bf0c71a779b 100644
--- a/pkg/msg/ctl.go
+++ b/pkg/msg/ctl.go
@@ -42,7 +42,3 @@ func ReadMsgInto(c io.Reader, msg Message) (err error) {
func WriteMsg(c io.Writer, msg interface{}) (err error) {
return msgCtl.WriteMsg(c, msg)
}
-
-func Pack(msg interface{}) (data []byte, err error) {
- return msgCtl.Pack(msg)
-}
diff --git a/pkg/nathole/controller.go b/pkg/nathole/controller.go
index 9c558326566..2eca5929455 100644
--- a/pkg/nathole/controller.go
+++ b/pkg/nathole/controller.go
@@ -115,7 +115,7 @@ func (c *Controller) CleanWorker(ctx context.Context) {
case <-ticker.C:
start := time.Now()
count, total := c.analyzer.Clean()
- log.Trace("clean %d/%d nathole analysis data, cost %v", count, total, time.Since(start))
+ log.Tracef("clean %d/%d nathole analysis data, cost %v", count, total, time.Since(start))
case <-ctx.Done():
return
}
@@ -191,11 +191,11 @@ func (c *Controller) HandleVisitor(m *msg.NatHoleVisitor, transporter transport.
return nil
}()
if err != nil {
- log.Warn("handle visitorMsg error: %v", err)
+ log.Warnf("handle visitorMsg error: %v", err)
_ = transporter.Send(c.GenNatHoleResponse(m.TransactionID, nil, err.Error()))
return
}
- log.Trace("handle visitor message, sid [%s], server name: %s", sid, m.ProxyName)
+ log.Tracef("handle visitor message, sid [%s], server name: %s", sid, m.ProxyName)
defer func() {
c.mu.Lock()
@@ -213,14 +213,14 @@ func (c *Controller) HandleVisitor(m *msg.NatHoleVisitor, transporter transport.
select {
case <-session.notifyCh:
case <-time.After(time.Duration(NatHoleTimeout) * time.Second):
- log.Debug("wait for NatHoleClient message timeout, sid [%s]", sid)
+ log.Debugf("wait for NatHoleClient message timeout, sid [%s]", sid)
return
}
// Make hole-punching decisions based on the NAT information of the client and visitor.
vResp, cResp, err := c.analysis(session)
if err != nil {
- log.Debug("sid [%s] analysis error: %v", err)
+ log.Debugf("sid [%s] analysis error: %v", err)
vResp = c.GenNatHoleResponse(session.visitorMsg.TransactionID, nil, err.Error())
cResp = c.GenNatHoleResponse(session.clientMsg.TransactionID, nil, err.Error())
}
@@ -257,7 +257,7 @@ func (c *Controller) HandleClient(m *msg.NatHoleClient, transporter transport.Me
if !ok {
return
}
- log.Trace("handle client message, sid [%s], server name: %s", session.sid, m.ProxyName)
+ log.Tracef("handle client message, sid [%s], server name: %s", session.sid, m.ProxyName)
session.clientMsg = m
session.clientTransporter = transporter
select {
@@ -271,13 +271,13 @@ func (c *Controller) HandleReport(m *msg.NatHoleReport) {
session, ok := c.sessions[m.Sid]
c.mu.RUnlock()
if !ok {
- log.Trace("sid [%s] report make hole success: %v, but session not found", m.Sid, m.Success)
+ log.Tracef("sid [%s] report make hole success: %v, but session not found", m.Sid, m.Success)
return
}
if m.Success {
c.analyzer.ReportSuccess(session.analysisKey, session.recommandMode, session.recommandIndex)
}
- log.Info("sid [%s] report make hole success: %v, mode %v, index %v",
+ log.Infof("sid [%s] report make hole success: %v, mode %v, index %v",
m.Sid, m.Success, session.recommandMode, session.recommandIndex)
}
@@ -359,10 +359,10 @@ func (c *Controller) analysis(session *Session) (*msg.NatHoleResp, *msg.NatHoleR
},
}
- log.Debug("sid [%s] visitor nat: %+v, candidateAddrs: %v; client nat: %+v, candidateAddrs: %v, protocol: %s",
+ log.Debugf("sid [%s] visitor nat: %+v, candidateAddrs: %v; client nat: %+v, candidateAddrs: %v, protocol: %s",
session.sid, *vNatFeature, vm.MappedAddrs, *cNatFeature, cm.MappedAddrs, protocol)
- log.Debug("sid [%s] visitor detect behavior: %+v", session.sid, vResp.DetectBehavior)
- log.Debug("sid [%s] client detect behavior: %+v", session.sid, cResp.DetectBehavior)
+ log.Debugf("sid [%s] visitor detect behavior: %+v", session.sid, vResp.DetectBehavior)
+ log.Debugf("sid [%s] client detect behavior: %+v", session.sid, cResp.DetectBehavior)
return vResp, cResp, nil
}
diff --git a/pkg/nathole/nathole.go b/pkg/nathole/nathole.go
index 065d0eb4a6c..bdd0ee83cb2 100644
--- a/pkg/nathole/nathole.go
+++ b/pkg/nathole/nathole.go
@@ -17,7 +17,7 @@ package nathole
import (
"context"
"fmt"
- "math/rand"
+ "math/rand/v2"
"net"
"slices"
"strconv"
@@ -204,7 +204,7 @@ func MakeHole(ctx context.Context, listenConn *net.UDPConn, m *msg.NatHoleResp,
for i := 0; i < m.DetectBehavior.ListenRandomPorts; i++ {
tmpConn, err := net.ListenUDP("udp4", nil)
if err != nil {
- xl.Warn("listen random udp addr error: %v", err)
+ xl.Warnf("listen random udp addr error: %v", err)
continue
}
listenConns = append(listenConns, tmpConn)
@@ -216,7 +216,7 @@ func MakeHole(ctx context.Context, listenConn *net.UDPConn, m *msg.NatHoleResp,
for _, detectAddr := range detectAddrs {
for _, conn := range listenConns {
if err := sendSidMessage(ctx, conn, m.Sid, transactionID, detectAddr, key, m.DetectBehavior.TTL); err != nil {
- xl.Trace("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
+ xl.Tracef("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
}
}
}
@@ -289,16 +289,16 @@ func waitDetectMessage(
if err != nil {
return nil, err
}
- xl.Debug("get udp message local %s, from %s", conn.LocalAddr(), raddr)
+ xl.Debugf("get udp message local %s, from %s", conn.LocalAddr(), raddr)
var m msg.NatHoleSid
if err := DecodeMessageInto(buf[:n], key, &m); err != nil {
- xl.Warn("decode sid message error: %v", err)
+ xl.Warnf("decode sid message error: %v", err)
continue
}
pool.PutBuf(buf)
if m.Sid != sid {
- xl.Warn("get sid message with wrong sid: %s, expect: %s", m.Sid, sid)
+ xl.Warnf("get sid message with wrong sid: %s, expect: %s", m.Sid, sid)
continue
}
@@ -311,7 +311,7 @@ func waitDetectMessage(
m.Response = true
buf2, err := EncodeMessage(&m, key)
if err != nil {
- xl.Warn("encode sid message error: %v", err)
+ xl.Warnf("encode sid message error: %v", err)
continue
}
_, _ = conn.WriteToUDP(buf2, raddr)
@@ -329,7 +329,7 @@ func sendSidMessage(
if ttl > 0 {
ttlStr = fmt.Sprintf(" with ttl %d", ttl)
}
- xl.Trace("send sid message from %s to %s%s", conn.LocalAddr(), addr, ttlStr)
+ xl.Tracef("send sid message from %s to %s%s", conn.LocalAddr(), addr, ttlStr)
raddr, err := net.ResolveUDPAddr("udp4", addr)
if err != nil {
return err
@@ -341,7 +341,7 @@ func sendSidMessage(
TransactionID: transactionID,
Sid: sid,
Response: false,
- Nonce: strings.Repeat("0", rand.Intn(20)),
+ Nonce: strings.Repeat("0", rand.IntN(20)),
}
buf, err := EncodeMessage(m, key)
if err != nil {
@@ -351,14 +351,14 @@ func sendSidMessage(
uConn := ipv4.NewConn(conn)
original, err := uConn.TTL()
if err != nil {
- xl.Trace("get ttl error %v", err)
+ xl.Tracef("get ttl error %v", err)
return err
}
- xl.Trace("original ttl %d", original)
+ xl.Tracef("original ttl %d", original)
err = uConn.SetTTL(ttl)
if err != nil {
- xl.Trace("set ttl error %v", err)
+ xl.Tracef("set ttl error %v", err)
} else {
defer func() {
_ = uConn.SetTTL(original)
@@ -382,7 +382,7 @@ func sendSidMessageToRangePorts(
for i := portsRange.From; i <= portsRange.To; i++ {
detectAddr := net.JoinHostPort(ip, strconv.Itoa(i))
if err := sendFunc(conn, detectAddr); err != nil {
- xl.Trace("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
+ xl.Tracef("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
}
time.Sleep(2 * time.Millisecond)
}
@@ -398,7 +398,7 @@ func sendSidMessageToRandomPorts(
used := sets.New[int]()
getUnusedPort := func() int {
for i := 0; i < 10; i++ {
- port := rand.Intn(65535-1024) + 1024
+ port := rand.IntN(65535-1024) + 1024
if !used.Has(port) {
used.Insert(port)
return port
@@ -422,7 +422,7 @@ func sendSidMessageToRandomPorts(
for _, ip := range slices.Compact(parseIPs(addrs)) {
detectAddr := net.JoinHostPort(ip, strconv.Itoa(port))
if err := sendFunc(conn, detectAddr); err != nil {
- xl.Trace("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
+ xl.Tracef("send sid message from %s to %s error: %v", conn.LocalAddr(), detectAddr, err)
}
time.Sleep(time.Millisecond * 15)
}
diff --git a/pkg/plugin/client/http2https.go b/pkg/plugin/client/http2https.go
index 7cd3cd4295f..65bc2140b64 100644
--- a/pkg/plugin/client/http2https.go
+++ b/pkg/plugin/client/http2https.go
@@ -54,6 +54,9 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
rp := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
+ r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
+ r.Out.Header["X-Forwarded-Host"] = r.In.Header["X-Forwarded-Host"]
+ r.Out.Header["X-Forwarded-Proto"] = r.In.Header["X-Forwarded-Proto"]
req := r.Out
req.URL.Scheme = "https"
req.URL.Host = p.opts.LocalAddr
diff --git a/pkg/plugin/client/https2http.go b/pkg/plugin/client/https2http.go
index 389ac849eb8..1dc3840180d 100644
--- a/pkg/plugin/client/https2http.go
+++ b/pkg/plugin/client/https2http.go
@@ -51,6 +51,8 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
rp := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
+ r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
+ r.SetXForwarded()
req := r.Out
req.URL.Scheme = "http"
req.URL.Host = p.opts.LocalAddr
@@ -98,8 +100,11 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
return config, nil
}
-func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
+func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
+ if extra.SrcAddr != nil {
+ wrapConn.SetRemoteAddr(extra.SrcAddr)
+ }
_ = p.l.PutConn(wrapConn)
}
diff --git a/pkg/plugin/client/https2https.go b/pkg/plugin/client/https2https.go
index da3302b80d2..dd3245f83be 100644
--- a/pkg/plugin/client/https2https.go
+++ b/pkg/plugin/client/https2https.go
@@ -56,6 +56,8 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
rp := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
+ r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
+ r.SetXForwarded()
req := r.Out
req.URL.Scheme = "https"
req.URL.Host = p.opts.LocalAddr
@@ -104,8 +106,11 @@ func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
return config, nil
}
-func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
+func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
+ if extra.SrcAddr != nil {
+ wrapConn.SetRemoteAddr(extra.SrcAddr)
+ }
_ = p.l.PutConn(wrapConn)
}
diff --git a/pkg/plugin/client/plugin.go b/pkg/plugin/client/plugin.go
index 520e379441f..0e5548e9f4e 100644
--- a/pkg/plugin/client/plugin.go
+++ b/pkg/plugin/client/plugin.go
@@ -50,6 +50,8 @@ func Create(name string, options v1.ClientPluginOptions) (p Plugin, err error) {
type ExtraInfo struct {
ProxyProtocolHeader *pp.Header
+ SrcAddr net.Addr
+ DstAddr net.Addr
}
type Plugin interface {
diff --git a/pkg/plugin/server/manager.go b/pkg/plugin/server/manager.go
index 516f407e1fd..ed96444ac22 100644
--- a/pkg/plugin/server/manager.go
+++ b/pkg/plugin/server/manager.go
@@ -86,7 +86,7 @@ func (m *Manager) Login(content *LoginContent) (*LoginContent, error) {
for _, p := range m.loginPlugins {
res, retContent, err = p.Handle(ctx, OpLogin, *content)
if err != nil {
- xl.Warn("send Login request to plugin [%s] error: %v", p.Name(), err)
+ xl.Warnf("send Login request to plugin [%s] error: %v", p.Name(), err)
return nil, errors.New("send Login request to plugin error")
}
if res.Reject {
@@ -120,7 +120,7 @@ func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) {
for _, p := range m.newProxyPlugins {
res, retContent, err = p.Handle(ctx, OpNewProxy, *content)
if err != nil {
- xl.Warn("send NewProxy request to plugin [%s] error: %v", p.Name(), err)
+ xl.Warnf("send NewProxy request to plugin [%s] error: %v", p.Name(), err)
return nil, errors.New("send NewProxy request to plugin error")
}
if res.Reject {
@@ -147,7 +147,7 @@ func (m *Manager) CloseProxy(content *CloseProxyContent) error {
for _, p := range m.closeProxyPlugins {
_, _, err := p.Handle(ctx, OpCloseProxy, *content)
if err != nil {
- xl.Warn("send CloseProxy request to plugin [%s] error: %v", p.Name(), err)
+ xl.Warnf("send CloseProxy request to plugin [%s] error: %v", p.Name(), err)
errs = append(errs, fmt.Sprintf("[%s]: %v", p.Name(), err))
}
}
@@ -179,7 +179,7 @@ func (m *Manager) Ping(content *PingContent) (*PingContent, error) {
for _, p := range m.pingPlugins {
res, retContent, err = p.Handle(ctx, OpPing, *content)
if err != nil {
- xl.Warn("send Ping request to plugin [%s] error: %v", p.Name(), err)
+ xl.Warnf("send Ping request to plugin [%s] error: %v", p.Name(), err)
return nil, errors.New("send Ping request to plugin error")
}
if res.Reject {
@@ -213,7 +213,7 @@ func (m *Manager) NewWorkConn(content *NewWorkConnContent) (*NewWorkConnContent,
for _, p := range m.newWorkConnPlugins {
res, retContent, err = p.Handle(ctx, OpNewWorkConn, *content)
if err != nil {
- xl.Warn("send NewWorkConn request to plugin [%s] error: %v", p.Name(), err)
+ xl.Warnf("send NewWorkConn request to plugin [%s] error: %v", p.Name(), err)
return nil, errors.New("send NewWorkConn request to plugin error")
}
if res.Reject {
@@ -247,7 +247,7 @@ func (m *Manager) NewUserConn(content *NewUserConnContent) (*NewUserConnContent,
for _, p := range m.newUserConnPlugins {
res, retContent, err = p.Handle(ctx, OpNewUserConn, *content)
if err != nil {
- xl.Info("send NewUserConn request to plugin [%s] error: %v", p.Name(), err)
+ xl.Infof("send NewUserConn request to plugin [%s] error: %v", p.Name(), err)
return nil, errors.New("send NewUserConn request to plugin error")
}
if res.Reject {
diff --git a/pkg/ssh/gateway.go b/pkg/ssh/gateway.go
index 90f2228ec18..5716f04038d 100644
--- a/pkg/ssh/gateway.go
+++ b/pkg/ssh/gateway.go
@@ -75,7 +75,7 @@ func NewGateway(
sshConfig.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
authorizedKeysMap, err := loadAuthorizedKeysFromFile(cfg.AuthorizedKeysFile)
if err != nil {
- log.Error("load authorized keys file error: %v", err)
+ log.Errorf("load authorized keys file error: %v", err)
return nil, fmt.Errorf("internal error")
}
@@ -120,7 +120,7 @@ func (g *Gateway) handleConn(conn net.Conn) {
return
}
if err := ts.Run(); err != nil {
- log.Error("ssh tunnel server run error: %v", err)
+ log.Errorf("ssh tunnel server run error: %v", err)
}
}
diff --git a/pkg/ssh/server.go b/pkg/ssh/server.go
index 7fb93c348c0..2adab17fb5a 100644
--- a/pkg/ssh/server.go
+++ b/pkg/ssh/server.go
@@ -123,7 +123,7 @@ func (s *TunnelServer) Run() error {
// join workConn and ssh channel
c, err := s.openConn(addr)
if err != nil {
- log.Trace("open conn error: %v", err)
+ log.Tracef("open conn error: %v", err)
workConn.Close()
return false
}
@@ -167,7 +167,7 @@ func (s *TunnelServer) Run() error {
if ps, err := s.waitProxyStatusReady(pc.GetBaseConfig().Name, time.Second); err != nil {
s.writeToClient(err.Error())
- log.Warn("wait proxy status ready error: %v", err)
+ log.Warnf("wait proxy status ready error: %v", err)
} else {
// success
s.writeToClient(createSuccessInfo(clientCfg.User, pc, ps))
@@ -175,7 +175,7 @@ func (s *TunnelServer) Run() error {
}
s.vc.Close()
- log.Trace("ssh tunnel connection from %v closed", sshConn.RemoteAddr())
+ log.Tracef("ssh tunnel connection from %v closed", sshConn.RemoteAddr())
s.closeDoneChOnce.Do(func() {
_ = sshConn.Close()
close(s.doneCh)
diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go
index a1391b3260a..f6399e00809 100644
--- a/pkg/util/log/log.go
+++ b/pkg/util/log/log.go
@@ -15,78 +15,65 @@
package log
import (
- "fmt"
+ "os"
- "github.com/fatedier/beego/logs"
+ "github.com/fatedier/golib/log"
)
-// Log is the under log object
-var Log *logs.BeeLogger
+var Logger *log.Logger
func init() {
- Log = logs.NewLogger(200)
- Log.EnableFuncCallDepth(true)
- Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
+ Logger = log.New(
+ log.WithCaller(true),
+ log.AddCallerSkip(1),
+ log.WithLevel(log.InfoLevel),
+ )
}
-func InitLog(logFile string, logLevel string, maxdays int64, disableLogColor bool) {
- SetLogFile(logFile, maxdays, disableLogColor)
- SetLogLevel(logLevel)
-}
-
-// SetLogFile to configure log params
-func SetLogFile(logFile string, maxdays int64, disableLogColor bool) {
- if logFile == "console" {
- params := ""
- if disableLogColor {
- params = `{"color": false}`
+func InitLogger(logPath string, levelStr string, maxDays int, disableLogColor bool) {
+ options := []log.Option{}
+ if logPath == "console" {
+ if !disableLogColor {
+ options = append(options,
+ log.WithOutput(log.NewConsoleWriter(log.ConsoleConfig{
+ Colorful: true,
+ }, os.Stdout)),
+ )
}
- _ = Log.SetLogger("console", params)
} else {
- params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays)
- _ = Log.SetLogger("file", params)
+ writer := log.NewRotateFileWriter(log.RotateFileConfig{
+ FileName: logPath,
+ Mode: log.RotateFileModeDaily,
+ MaxDays: maxDays,
+ })
+ writer.Init()
+ options = append(options, log.WithOutput(writer))
}
-}
-// SetLogLevel set log level, default is warning
-// value: error, warning, info, debug, trace
-func SetLogLevel(logLevel string) {
- var level int
- switch logLevel {
- case "error":
- level = 3
- case "warn":
- level = 4
- case "info":
- level = 6
- case "debug":
- level = 7
- case "trace":
- level = 8
- default:
- level = 4 // warning
+ level, err := log.ParseLevel(levelStr)
+ if err != nil {
+ level = log.InfoLevel
}
- Log.SetLevel(level)
+ options = append(options, log.WithLevel(level))
+ Logger = Logger.WithOptions(options...)
}
-// wrap log
-
-func Error(format string, v ...interface{}) {
- Log.Error(format, v...)
+func Errorf(format string, v ...interface{}) {
+ Logger.Errorf(format, v...)
}
-func Warn(format string, v ...interface{}) {
- Log.Warn(format, v...)
+func Warnf(format string, v ...interface{}) {
+ Logger.Warnf(format, v...)
}
-func Info(format string, v ...interface{}) {
- Log.Info(format, v...)
+func Infof(format string, v ...interface{}) {
+ Logger.Infof(format, v...)
}
-func Debug(format string, v ...interface{}) {
- Log.Debug(format, v...)
+func Debugf(format string, v ...interface{}) {
+ Logger.Debugf(format, v...)
}
-func Trace(format string, v ...interface{}) {
- Log.Trace(format, v...)
+func Tracef(format string, v ...interface{}) {
+ Logger.Tracef(format, v...)
}
diff --git a/pkg/util/net/conn.go b/pkg/util/net/conn.go
index a5bbe7371c5..20ac73ed1c6 100644
--- a/pkg/util/net/conn.go
+++ b/pkg/util/net/conn.go
@@ -76,9 +76,11 @@ type WrapReadWriteCloserConn struct {
io.ReadWriteCloser
underConn net.Conn
+
+ remoteAddr net.Addr
}
-func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) net.Conn {
+func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) *WrapReadWriteCloserConn {
return &WrapReadWriteCloserConn{
ReadWriteCloser: rwc,
underConn: underConn,
@@ -92,7 +94,14 @@ func (conn *WrapReadWriteCloserConn) LocalAddr() net.Addr {
return (*net.TCPAddr)(nil)
}
+func (conn *WrapReadWriteCloserConn) SetRemoteAddr(addr net.Addr) {
+ conn.remoteAddr = addr
+}
+
func (conn *WrapReadWriteCloserConn) RemoteAddr() net.Addr {
+ if conn.remoteAddr != nil {
+ return conn.remoteAddr
+ }
if conn.underConn != nil {
return conn.underConn.RemoteAddr()
}
diff --git a/pkg/util/net/dns.go b/pkg/util/net/dns.go
index 5e1d5ccbfc1..441b1e67a3f 100644
--- a/pkg/util/net/dns.go
+++ b/pkg/util/net/dns.go
@@ -26,8 +26,8 @@ func SetDefaultDNSAddress(dnsAddress string) {
// Change default dns server
net.DefaultResolver = &net.Resolver{
PreferGo: true,
- Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
- return net.Dial("udp", dnsAddress)
+ Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
+ return net.Dial(network, dnsAddress)
},
}
}
diff --git a/pkg/util/net/http.go b/pkg/util/net/http.go
index 642d15901e3..e9fc52609fc 100644
--- a/pkg/util/net/http.go
+++ b/pkg/util/net/http.go
@@ -24,30 +24,6 @@ import (
"github.com/fatedier/frp/pkg/util/util"
)
-type HTTPAuthWrapper struct {
- h http.Handler
- user string
- passwd string
-}
-
-func NewHTTPBasicAuthWrapper(h http.Handler, user, passwd string) http.Handler {
- return &HTTPAuthWrapper{
- h: h,
- user: user,
- passwd: passwd,
- }
-}
-
-func (aw *HTTPAuthWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- user, passwd, hasAuth := r.BasicAuth()
- if (aw.user == "" && aw.passwd == "") || (hasAuth && user == aw.user && passwd == aw.passwd) {
- aw.h.ServeHTTP(w, r)
- } else {
- w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
- http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
- }
-}
-
type HTTPAuthMiddleware struct {
user string
passwd string
diff --git a/pkg/util/net/websocket.go b/pkg/util/net/websocket.go
index 6a8d369c4a9..e642be06f3c 100644
--- a/pkg/util/net/websocket.go
+++ b/pkg/util/net/websocket.go
@@ -4,7 +4,6 @@ import (
"errors"
"net"
"net/http"
- "strconv"
"golang.org/x/net/websocket"
)
@@ -50,15 +49,6 @@ func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
return
}
-func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) {
- tcpLn, err := net.Listen("tcp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort)))
- if err != nil {
- return nil, err
- }
- l := NewWebsocketListener(tcpLn)
- return l, nil
-}
-
func (p *WebsocketListener) Accept() (net.Conn, error) {
c, ok := <-p.acceptCh
if !ok {
diff --git a/pkg/util/system/system.go b/pkg/util/system/system.go
new file mode 100644
index 00000000000..ae40e85e214
--- /dev/null
+++ b/pkg/util/system/system.go
@@ -0,0 +1,22 @@
+// Copyright 2024 The frp Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build !android
+
+package system
+
+// EnableCompatibilityMode enables compatibility mode for different system.
+// For example, on Android, the inability to obtain the correct time zone will result in incorrect log time output.
+func EnableCompatibilityMode() {
+}
diff --git a/pkg/util/system/system_android.go b/pkg/util/system/system_android.go
new file mode 100644
index 00000000000..bfcf401dfb5
--- /dev/null
+++ b/pkg/util/system/system_android.go
@@ -0,0 +1,66 @@
+// Copyright 2024 The frp Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package system
+
+import (
+ "context"
+ "net"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+func EnableCompatibilityMode() {
+ fixTimezone()
+ fixDNSResolver()
+}
+
+// fixTimezone is used to try our best to fix timezone issue on some Android devices.
+func fixTimezone() {
+ out, err := exec.Command("/system/bin/getprop", "persist.sys.timezone").Output()
+ if err != nil {
+ return
+ }
+ loc, err := time.LoadLocation(strings.TrimSpace(string(out)))
+ if err != nil {
+ return
+ }
+ time.Local = loc
+}
+
+// fixDNSResolver will first attempt to resolve google.com to check if the current DNS is available.
+// If it is not available, it will default to using 8.8.8.8 as the DNS server.
+// This is a workaround for the issue that golang can't get the default DNS servers on Android.
+func fixDNSResolver() {
+ // First, we attempt to resolve a domain. If resolution is successful, no modifications are necessary.
+ // In real-world scenarios, users may have already configured /etc/resolv.conf, or compiled directly
+ // in the Android environment instead of using cross-platform compilation, so this issue does not arise.
+ if net.DefaultResolver != nil {
+ timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
+ _, err := net.DefaultResolver.LookupHost(timeoutCtx, "google.com")
+ if err == nil {
+ return
+ }
+ }
+ // If the resolution fails, use 8.8.8.8 as the DNS server.
+ // Note: If there are other methods to obtain the default DNS servers, the default DNS servers should be used preferentially.
+ net.DefaultResolver = &net.Resolver{
+ PreferGo: true,
+ Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
+ return net.Dial(network, "8.8.8.8:53")
+ },
+ }
+}
diff --git a/pkg/util/util/util.go b/pkg/util/util/util.go
index d2562b01a27..7758054d948 100644
--- a/pkg/util/util/util.go
+++ b/pkg/util/util/util.go
@@ -20,7 +20,7 @@ import (
"crypto/subtle"
"encoding/hex"
"fmt"
- mathrand "math/rand"
+ mathrand "math/rand/v2"
"net"
"strconv"
"strings"
@@ -47,16 +47,6 @@ func RandIDWithLen(idLen int) (id string, err error) {
return id[:idLen], nil
}
-// RandIDWithRandLen return a rand string with length between [start, end).
-func RandIDWithRandLen(start, end int) (id string, err error) {
- if start >= end {
- err = fmt.Errorf("start should be less than end")
- return
- }
- idLen := mathrand.Intn(end-start) + start
- return RandIDWithLen(idLen)
-}
-
func GetAuthKey(token string, timestamp int64) (key string) {
md5Ctx := md5.New()
md5Ctx.Write([]byte(token))
@@ -134,7 +124,7 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
if max <= min {
n = min
} else {
- n = mathrand.Int63n(max-min) + min
+ n = mathrand.Int64N(max-min) + min
}
d := duration * time.Duration(n) / time.Duration(1000)
time.Sleep(d)
diff --git a/pkg/util/util/util_test.go b/pkg/util/util/util_test.go
index cf76485b281..0061861119e 100644
--- a/pkg/util/util/util_test.go
+++ b/pkg/util/util/util_test.go
@@ -14,48 +14,6 @@ func TestRandId(t *testing.T) {
assert.Equal(16, len(id))
}
-func TestRandIDWithRandLen(t *testing.T) {
- tests := []struct {
- name string
- start int
- end int
- expectErr bool
- }{
- {
- name: "start and end are equal",
- start: 5,
- end: 5,
- expectErr: true,
- },
- {
- name: "start is less than end",
- start: 5,
- end: 10,
- expectErr: false,
- },
- {
- name: "start is greater than end",
- start: 10,
- end: 5,
- expectErr: true,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert := assert.New(t)
- id, err := RandIDWithRandLen(tt.start, tt.end)
- if tt.expectErr {
- assert.Error(err)
- } else {
- assert.NoError(err)
- assert.GreaterOrEqual(len(id), tt.start)
- assert.Less(len(id), tt.end)
- }
- })
- }
-}
-
func TestGetAuthKey(t *testing.T) {
assert := assert.New(t)
key := GetAuthKey("1234", 1488720000)
diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go
index 1dc5904c0e4..90af7813120 100644
--- a/pkg/util/version/version.go
+++ b/pkg/util/version/version.go
@@ -14,34 +14,8 @@
package version
-import (
- "strconv"
- "strings"
-)
-
-var version = "0.54.0"
+var version = "0.56.0"
func Full() string {
return version
}
-
-func getSubVersion(v string, position int) int64 {
- arr := strings.Split(v, ".")
- if len(arr) < 3 {
- return 0
- }
- res, _ := strconv.ParseInt(arr[position], 10, 64)
- return res
-}
-
-func Proto(v string) int64 {
- return getSubVersion(v, 0)
-}
-
-func Major(v string) int64 {
- return getSubVersion(v, 1)
-}
-
-func Minor(v string) int64 {
- return getSubVersion(v, 2)
-}
diff --git a/pkg/util/version/version_test.go b/pkg/util/version/version_test.go
deleted file mode 100644
index 2b4077cf33b..00000000000
--- a/pkg/util/version/version_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2016 fatedier, fatedier@gmail.com
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package version
-
-import (
- "fmt"
- "strconv"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestFull(t *testing.T) {
- assert := assert.New(t)
- version := Full()
- arr := strings.Split(version, ".")
- assert.Equal(3, len(arr))
-
- proto, err := strconv.ParseInt(arr[0], 10, 64)
- assert.NoError(err)
- assert.True(proto >= 0)
-
- major, err := strconv.ParseInt(arr[1], 10, 64)
- assert.NoError(err)
- assert.True(major >= 0)
-
- minor, err := strconv.ParseInt(arr[2], 10, 64)
- assert.NoError(err)
- assert.True(minor >= 0)
-}
-
-func TestVersion(t *testing.T) {
- assert := assert.New(t)
- proto := Proto(Full())
- major := Major(Full())
- minor := Minor(Full())
- parseVersion := fmt.Sprintf("%d.%d.%d", proto, major, minor)
- version := Full()
- assert.Equal(parseVersion, version)
-}
diff --git a/pkg/util/vhost/http.go b/pkg/util/vhost/http.go
index 7aa67c98f36..7afc7ebbf19 100644
--- a/pkg/util/vhost/http.go
+++ b/pkg/util/vhost/http.go
@@ -20,7 +20,7 @@ import (
"encoding/base64"
"errors"
"fmt"
- "log"
+ stdlog "log"
"net"
"net/http"
"net/http/httputil"
@@ -32,7 +32,7 @@ import (
"github.com/fatedier/golib/pool"
httppkg "github.com/fatedier/frp/pkg/util/http"
- logpkg "github.com/fatedier/frp/pkg/util/log"
+ "github.com/fatedier/frp/pkg/util/log"
)
var ErrNoRouteFound = errors.New("no route found")
@@ -59,6 +59,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
proxy := &httputil.ReverseProxy{
// Modify incoming requests by route policies.
Rewrite: func(r *httputil.ProxyRequest) {
+ r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
r.SetXForwarded()
req := r.Out
req.URL.Scheme = "http"
@@ -76,7 +77,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
// ignore error here, it will use CreateConnFn instead later
endpoint, _ = rc.ChooseEndpointFn()
reqRouteInfo.Endpoint = endpoint
- logpkg.Trace("choose endpoint name [%s] for http request host [%s] path [%s] httpuser [%s]",
+ log.Tracef("choose endpoint name [%s] for http request host [%s] path [%s] httpuser [%s]",
endpoint, oldHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
}
// Set {domain}.{location}.{routeByHTTPUser}.{endpoint} as URL host here to let http transport reuse connections.
@@ -116,9 +117,9 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
},
},
BufferPool: newWrapPool(),
- ErrorLog: log.New(newWrapLogger(), "", 0),
+ ErrorLog: stdlog.New(newWrapLogger(), "", 0),
ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
- logpkg.Warn("do http proxy request [host: %s] error: %v", req.Host, err)
+ log.Warnf("do http proxy request [host: %s] error: %v", req.Host, err)
rw.WriteHeader(http.StatusNotFound)
_, _ = rw.Write(getNotFoundPageContent())
},
@@ -145,7 +146,7 @@ func (rp *HTTPReverseProxy) UnRegister(routeCfg RouteConfig) {
func (rp *HTTPReverseProxy) GetRouteConfig(domain, location, routeByHTTPUser string) *RouteConfig {
vr, ok := rp.getVhost(domain, location, routeByHTTPUser)
if ok {
- logpkg.Debug("get new HTTP request host [%s] path [%s] httpuser [%s]", domain, location, routeByHTTPUser)
+ log.Debugf("get new HTTP request host [%s] path [%s] httpuser [%s]", domain, location, routeByHTTPUser)
return vr.payload.(*RouteConfig)
}
return nil
@@ -335,6 +336,6 @@ type wrapLogger struct{}
func newWrapLogger() *wrapLogger { return &wrapLogger{} }
func (l *wrapLogger) Write(p []byte) (n int, err error) {
- logpkg.Warn("%s", string(bytes.TrimRight(p, "\n")))
+ log.Warnf("%s", string(bytes.TrimRight(p, "\n")))
return len(p), nil
}
diff --git a/pkg/util/vhost/resource.go b/pkg/util/vhost/resource.go
index bf91e13358f..a65e29973ea 100644
--- a/pkg/util/vhost/resource.go
+++ b/pkg/util/vhost/resource.go
@@ -20,7 +20,7 @@ import (
"net/http"
"os"
- logpkg "github.com/fatedier/frp/pkg/util/log"
+ "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/version"
)
@@ -58,7 +58,7 @@ func getNotFoundPageContent() []byte {
if NotFoundPagePath != "" {
buf, err = os.ReadFile(NotFoundPagePath)
if err != nil {
- logpkg.Warn("read custom 404 page error: %v", err)
+ log.Warnf("read custom 404 page error: %v", err)
buf = []byte(NotFound)
}
} else {
diff --git a/pkg/util/vhost/vhost.go b/pkg/util/vhost/vhost.go
index d529e4249e3..b1370599a70 100644
--- a/pkg/util/vhost/vhost.go
+++ b/pkg/util/vhost/vhost.go
@@ -203,7 +203,7 @@ func (v *Muxer) handle(c net.Conn) {
sConn, reqInfoMap, err := v.vhostFunc(c)
if err != nil {
- log.Debug("get hostname from http/https request error: %v", err)
+ log.Debugf("get hostname from http/https request error: %v", err)
_ = c.Close()
return
}
@@ -213,7 +213,7 @@ func (v *Muxer) handle(c net.Conn) {
httpUser := reqInfoMap["HTTPUser"]
l, ok := v.getListener(name, path, httpUser)
if !ok {
- log.Debug("http request for host [%s] path [%s] httpUser [%s] not found", name, path, httpUser)
+ log.Debugf("http request for host [%s] path [%s] httpUser [%s] not found", name, path, httpUser)
v.failHook(sConn)
return
}
@@ -221,7 +221,7 @@ func (v *Muxer) handle(c net.Conn) {
xl := xlog.FromContextSafe(l.ctx)
if v.successHook != nil {
if err := v.successHook(c, reqInfoMap); err != nil {
- xl.Info("success func failure on vhost connection: %v", err)
+ xl.Infof("success func failure on vhost connection: %v", err)
_ = c.Close()
return
}
@@ -232,7 +232,7 @@ func (v *Muxer) handle(c net.Conn) {
if l.mux.checkAuth != nil && l.username != "" {
ok, err := l.mux.checkAuth(c, l.username, l.password, reqInfoMap)
if !ok || err != nil {
- xl.Debug("auth failed for user: %s", l.username)
+ xl.Debugf("auth failed for user: %s", l.username)
_ = c.Close()
return
}
@@ -244,12 +244,12 @@ func (v *Muxer) handle(c net.Conn) {
}
c = sConn
- xl.Debug("new request host [%s] path [%s] httpUser [%s]", name, path, httpUser)
+ xl.Debugf("new request host [%s] path [%s] httpUser [%s]", name, path, httpUser)
err = errors.PanicToError(func() {
l.accept <- c
})
if err != nil {
- xl.Warn("listener is already closed, ignore this request")
+ xl.Warnf("listener is already closed, ignore this request")
}
}
@@ -278,10 +278,10 @@ func (l *Listener) Accept() (net.Conn, error) {
if l.mux.rewriteHost != nil {
sConn, err := l.mux.rewriteHost(conn, l.rewriteHost)
if err != nil {
- xl.Warn("host header rewrite failed: %v", err)
+ xl.Warnf("host header rewrite failed: %v", err)
return nil, fmt.Errorf("host header rewrite failed")
}
- xl.Debug("rewrite host to [%s] success", l.rewriteHost)
+ xl.Debugf("rewrite host to [%s] success", l.rewriteHost)
conn = sConn
}
return netpkg.NewContextConn(l.ctx, conn), nil
diff --git a/pkg/util/wait/backoff.go b/pkg/util/wait/backoff.go
index e07c5316ff6..4d01ace328c 100644
--- a/pkg/util/wait/backoff.go
+++ b/pkg/util/wait/backoff.go
@@ -15,8 +15,7 @@
package wait
import (
- "math/rand"
- "sync"
+ "math/rand/v2"
"time"
"github.com/fatedier/frp/pkg/util/util"
@@ -174,21 +173,3 @@ func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
return period
}), true, stopCh)
}
-
-func MergeAndCloseOnAnyStopChannel[T any](upstreams ...<-chan T) <-chan T {
- out := make(chan T)
- closeOnce := sync.Once{}
- for _, upstream := range upstreams {
- ch := upstream
- go func() {
- select {
- case <-ch:
- closeOnce.Do(func() {
- close(out)
- })
- case <-out:
- }
- }()
- }
- return out
-}
diff --git a/pkg/util/xlog/xlog.go b/pkg/util/xlog/xlog.go
index 56b07522eb7..184c0c1f7a5 100644
--- a/pkg/util/xlog/xlog.go
+++ b/pkg/util/xlog/xlog.go
@@ -94,22 +94,22 @@ func (l *Logger) Spawn() *Logger {
return nl
}
-func (l *Logger) Error(format string, v ...interface{}) {
- log.Log.Error(l.prefixString+format, v...)
+func (l *Logger) Errorf(format string, v ...interface{}) {
+ log.Logger.Errorf(l.prefixString+format, v...)
}
-func (l *Logger) Warn(format string, v ...interface{}) {
- log.Log.Warn(l.prefixString+format, v...)
+func (l *Logger) Warnf(format string, v ...interface{}) {
+ log.Logger.Warnf(l.prefixString+format, v...)
}
-func (l *Logger) Info(format string, v ...interface{}) {
- log.Log.Info(l.prefixString+format, v...)
+func (l *Logger) Infof(format string, v ...interface{}) {
+ log.Logger.Infof(l.prefixString+format, v...)
}
-func (l *Logger) Debug(format string, v ...interface{}) {
- log.Log.Debug(l.prefixString+format, v...)
+func (l *Logger) Debugf(format string, v ...interface{}) {
+ log.Logger.Debugf(l.prefixString+format, v...)
}
-func (l *Logger) Trace(format string, v ...interface{}) {
- log.Log.Trace(l.prefixString+format, v...)
+func (l *Logger) Tracef(format string, v ...interface{}) {
+ log.Logger.Tracef(l.prefixString+format, v...)
}
diff --git a/server/control.go b/server/control.go
index dbb1af0a038..ea8a34c17ef 100644
--- a/server/control.go
+++ b/server/control.go
@@ -224,7 +224,7 @@ func (ctl *Control) Close() error {
func (ctl *Control) Replaced(newCtl *Control) {
xl := ctl.xl
- xl.Info("Replaced by client [%s]", newCtl.runID)
+ xl.Infof("Replaced by client [%s]", newCtl.runID)
ctl.runID = ""
ctl.conn.Close()
}
@@ -233,17 +233,17 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) error {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
- xl.Error("panic error: %v", err)
- xl.Error(string(debug.Stack()))
+ xl.Errorf("panic error: %v", err)
+ xl.Errorf(string(debug.Stack()))
}
}()
select {
case ctl.workConnCh <- conn:
- xl.Debug("new work connection registered")
+ xl.Debugf("new work connection registered")
return nil
default:
- xl.Debug("work connection pool is full, discarding")
+ xl.Debugf("work connection pool is full, discarding")
return fmt.Errorf("work connection pool is full, discarding")
}
}
@@ -256,8 +256,8 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
- xl.Error("panic error: %v", err)
- xl.Error(string(debug.Stack()))
+ xl.Errorf("panic error: %v", err)
+ xl.Errorf(string(debug.Stack()))
}
}()
@@ -269,7 +269,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
err = pkgerr.ErrCtlClosed
return
}
- xl.Debug("get work connection from pool")
+ xl.Debugf("get work connection from pool")
default:
// no work connections available in the poll, send message to frpc to get more
if err := ctl.msgDispatcher.Send(&msg.ReqWorkConn{}); err != nil {
@@ -280,13 +280,13 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
case workConn, ok = <-ctl.workConnCh:
if !ok {
err = pkgerr.ErrCtlClosed
- xl.Warn("no work connections available, %v", err)
+ xl.Warnf("no work connections available, %v", err)
return
}
case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
err = fmt.Errorf("timeout trying to get work connection")
- xl.Warn("%v", err)
+ xl.Warnf("%v", err)
return
}
}
@@ -305,7 +305,7 @@ func (ctl *Control) heartbeatWorker() {
if !lo.FromPtr(ctl.serverCfg.Transport.TCPMux) && ctl.serverCfg.Transport.HeartbeatTimeout > 0 {
go wait.Until(func() {
if time.Since(ctl.lastPing.Load().(time.Time)) > time.Duration(ctl.serverCfg.Transport.HeartbeatTimeout)*time.Second {
- xl.Warn("heartbeat timeout")
+ xl.Warnf("heartbeat timeout")
ctl.conn.Close()
return
}
@@ -356,7 +356,7 @@ func (ctl *Control) worker() {
}
metrics.Server.CloseClient()
- xl.Info("client exit success")
+ xl.Infof("client exit success")
close(ctl.doneCh)
}
@@ -393,12 +393,12 @@ func (ctl *Control) handleNewProxy(m msg.Message) {
ProxyName: inMsg.ProxyName,
}
if err != nil {
- xl.Warn("new proxy [%s] type [%s] error: %v", inMsg.ProxyName, inMsg.ProxyType, err)
+ xl.Warnf("new proxy [%s] type [%s] error: %v", inMsg.ProxyName, inMsg.ProxyType, err)
resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", inMsg.ProxyName),
err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient))
} else {
resp.RemoteAddr = remoteAddr
- xl.Info("new proxy [%s] type [%s] success", inMsg.ProxyName, inMsg.ProxyType)
+ xl.Infof("new proxy [%s] type [%s] success", inMsg.ProxyName, inMsg.ProxyType)
metrics.Server.NewProxy(inMsg.ProxyName, inMsg.ProxyType)
}
_ = ctl.msgDispatcher.Send(resp)
@@ -422,14 +422,14 @@ func (ctl *Control) handlePing(m msg.Message) {
err = ctl.authVerifier.VerifyPing(inMsg)
}
if err != nil {
- xl.Warn("received invalid ping: %v", err)
+ xl.Warnf("received invalid ping: %v", err)
_ = ctl.msgDispatcher.Send(&msg.Pong{
Error: util.GenerateResponseErrorString("invalid ping", err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient)),
})
return
}
ctl.lastPing.Store(time.Now())
- xl.Debug("receive heartbeat")
+ xl.Debugf("receive heartbeat")
_ = ctl.msgDispatcher.Send(&msg.Pong{})
}
@@ -452,7 +452,7 @@ func (ctl *Control) handleCloseProxy(m msg.Message) {
xl := ctl.xl
inMsg := m.(*msg.CloseProxy)
_ = ctl.CloseProxy(inMsg)
- xl.Info("close proxy [%s] success", inMsg.ProxyName)
+ xl.Infof("close proxy [%s] success", inMsg.ProxyName)
}
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
diff --git a/server/dashboard_api.go b/server/dashboard_api.go
index 9ecd5a7c265..62415c968d2 100644
--- a/server/dashboard_api.go
+++ b/server/dashboard_api.go
@@ -99,14 +99,14 @@ func (svr *Service) healthz(w http.ResponseWriter, _ *http.Request) {
func (svr *Service) apiServerInfo(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
defer func() {
- log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
+ log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
- log.Info("Http request: [%s]", r.URL.Path)
+ log.Infof("Http request: [%s]", r.URL.Path)
serverStats := mem.StatsCollector.GetServer()
svrResp := serverInfoResp{
Version: version.Full(),
@@ -219,13 +219,13 @@ func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
proxyType := params["type"]
defer func() {
- log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
+ log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
- log.Info("Http request: [%s]", r.URL.Path)
+ log.Infof("Http request: [%s]", r.URL.Path)
proxyInfoResp := GetProxyInfoResp{}
proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType)
@@ -245,12 +245,12 @@ func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxySt
if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok {
content, err := json.Marshal(pxy.GetConfigurer())
if err != nil {
- log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
+ log.Warnf("marshal proxy [%s] conf info error: %v", ps.Name, err)
continue
}
proxyInfo.Conf = getConfByType(ps.Type)
if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
- log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
+ log.Warnf("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
continue
}
proxyInfo.Status = "online"
@@ -291,13 +291,13 @@ func (svr *Service) apiProxyByTypeAndName(w http.ResponseWriter, r *http.Request
name := params["name"]
defer func() {
- log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
+ log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
- log.Info("Http request: [%s]", r.URL.Path)
+ log.Infof("Http request: [%s]", r.URL.Path)
var proxyStatsResp GetProxyStatsResp
proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name)
@@ -319,14 +319,14 @@ func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName strin
if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
content, err := json.Marshal(pxy.GetConfigurer())
if err != nil {
- log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
+ log.Warnf("marshal proxy [%s] conf info error: %v", ps.Name, err)
code = 400
msg = "parse conf error"
return
}
proxyInfo.Conf = getConfByType(ps.Type)
if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil {
- log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
+ log.Warnf("unmarshal proxy [%s] conf info error: %v", ps.Name, err)
code = 400
msg = "parse conf error"
return
@@ -359,13 +359,13 @@ func (svr *Service) apiProxyTraffic(w http.ResponseWriter, r *http.Request) {
name := params["name"]
defer func() {
- log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
+ log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
- log.Info("Http request: [%s]", r.URL.Path)
+ log.Infof("Http request: [%s]", r.URL.Path)
trafficResp := GetProxyTrafficResp{}
trafficResp.Name = name
@@ -387,9 +387,9 @@ func (svr *Service) apiProxyTraffic(w http.ResponseWriter, r *http.Request) {
func (svr *Service) deleteProxies(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
- log.Info("Http request: [%s]", r.URL.Path)
+ log.Infof("Http request: [%s]", r.URL.Path)
defer func() {
- log.Info("Http response [%s]: code [%d]", r.URL.Path, res.Code)
+ log.Infof("Http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
@@ -403,5 +403,5 @@ func (svr *Service) deleteProxies(w http.ResponseWriter, r *http.Request) {
return
}
cleared, total := mem.StatsCollector.ClearOfflineProxies()
- log.Info("cleared [%d] offline proxies, total [%d] proxies", cleared, total)
+ log.Infof("cleared [%d] offline proxies, total [%d] proxies", cleared, total)
}
diff --git a/server/proxy/http.go b/server/proxy/http.go
index 44a462b7ecc..cd4c4b968bc 100644
--- a/server/proxy/http.go
+++ b/server/proxy/http.go
@@ -107,7 +107,7 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
})
}
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPPort))
- xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
+ xl.Infof("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
routeConfig.Domain, routeConfig.Location, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
}
}
@@ -140,7 +140,7 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
}
addrs = append(addrs, util.CanonicalAddr(tmpRouteConfig.Domain, pxy.serverCfg.VhostHTTPPort))
- xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
+ xl.Infof("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
routeConfig.Domain, routeConfig.Location, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
}
}
@@ -152,7 +152,7 @@ func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err
xl := pxy.xl
rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
if errRet != nil {
- xl.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
+ xl.Warnf("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
// we do not return error here since remoteAddr is not necessary for proxies without proxy protocol enabled
}
@@ -166,7 +166,7 @@ func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err
if pxy.cfg.Transport.UseEncryption {
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Auth.Token))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
diff --git a/server/proxy/https.go b/server/proxy/https.go
index dc9b77ebe00..4575ac13d08 100644
--- a/server/proxy/https.go
+++ b/server/proxy/https.go
@@ -64,7 +64,7 @@ func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
err = errRet
return
}
- xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
+ xl.Infof("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort))
}
@@ -76,7 +76,7 @@ func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
err = errRet
return
}
- xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
+ xl.Infof("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort))
}
diff --git a/server/proxy/proxy.go b/server/proxy/proxy.go
index f5c850e98af..d5ab0f13ae5 100644
--- a/server/proxy/proxy.go
+++ b/server/proxy/proxy.go
@@ -112,7 +112,7 @@ func (pxy *BaseProxy) GetConfigurer() v1.ProxyConfigurer {
func (pxy *BaseProxy) Close() {
xl := xlog.FromContextSafe(pxy.ctx)
- xl.Info("proxy closing")
+ xl.Infof("proxy closing")
for _, l := range pxy.listeners {
l.Close()
}
@@ -125,10 +125,10 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
// try all connections from the pool
for i := 0; i < pxy.poolCount+1; i++ {
if workConn, err = pxy.getWorkConnFn(); err != nil {
- xl.Warn("failed to get work connection: %v", err)
+ xl.Warnf("failed to get work connection: %v", err)
return
}
- xl.Debug("get a new work connection: [%s]", workConn.RemoteAddr().String())
+ xl.Debugf("get a new work connection: [%s]", workConn.RemoteAddr().String())
xl.Spawn().AppendPrefix(pxy.GetName())
workConn = netpkg.NewContextConn(pxy.ctx, workConn)
@@ -158,7 +158,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
Error: "",
})
if err != nil {
- xl.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
+ xl.Warnf("failed to send message to work connection from pool: %v, times: %d", err, i)
workConn.Close()
} else {
break
@@ -166,7 +166,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
}
if err != nil {
- xl.Error("try to get work connection failed in the end")
+ xl.Errorf("try to get work connection failed in the end")
return
}
return
@@ -193,15 +193,15 @@ func (pxy *BaseProxy) startCommonTCPListenersHandler() {
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
- xl.Info("met temporary error: %s, sleep for %s ...", err, tempDelay)
+ xl.Infof("met temporary error: %s, sleep for %s ...", err, tempDelay)
time.Sleep(tempDelay)
continue
}
- xl.Warn("listener is closed: %s", err)
+ xl.Warnf("listener is closed: %s", err)
return
}
- xl.Info("get a user connection [%s]", c.RemoteAddr().String())
+ xl.Infof("get a user connection [%s]", c.RemoteAddr().String())
go pxy.handleUserTCPConnection(c)
}
}(listener)
@@ -225,7 +225,7 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
}
_, err := rc.PluginManager.NewUserConn(content)
if err != nil {
- xl.Warn("the user conn [%s] was rejected, err:%v", content.RemoteAddr, err)
+ xl.Warnf("the user conn [%s] was rejected, err:%v", content.RemoteAddr, err)
return
}
@@ -237,12 +237,12 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
defer workConn.Close()
var local io.ReadWriteCloser = workConn
- xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t",
+ xl.Tracef("handler user tcp connection, use_encryption: %t, use_compression: %t",
cfg.Transport.UseEncryption, cfg.Transport.UseCompression)
if cfg.Transport.UseEncryption {
local, err = libio.WithEncryption(local, []byte(serverCfg.Auth.Token))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
return
}
}
@@ -258,7 +258,7 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
})
}
- xl.Debug("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
+ xl.Debugf("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
name := pxy.GetName()
@@ -268,7 +268,7 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
metrics.Server.CloseConnection(name, proxyType)
metrics.Server.AddTrafficIn(name, proxyType, inCount)
metrics.Server.AddTrafficOut(name, proxyType, outCount)
- xl.Debug("join connections closed")
+ xl.Debugf("join connections closed")
}
type Options struct {
diff --git a/server/proxy/stcp.go b/server/proxy/stcp.go
index 4f08a7c12c3..06b1b17f47b 100644
--- a/server/proxy/stcp.go
+++ b/server/proxy/stcp.go
@@ -53,7 +53,7 @@ func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
return
}
pxy.listeners = append(pxy.listeners, listener)
- xl.Info("stcp proxy custom listen success")
+ xl.Infof("stcp proxy custom listen success")
pxy.startCommonTCPListenersHandler()
return
diff --git a/server/proxy/sudp.go b/server/proxy/sudp.go
index a6d7c798f1b..f37fb4239fd 100644
--- a/server/proxy/sudp.go
+++ b/server/proxy/sudp.go
@@ -53,7 +53,7 @@ func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
return
}
pxy.listeners = append(pxy.listeners, listener)
- xl.Info("sudp proxy custom listen success")
+ xl.Infof("sudp proxy custom listen success")
pxy.startCommonTCPListenersHandler()
return
diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go
index 4196ad4ae3a..a6eae3a9d6b 100644
--- a/server/proxy/tcp.go
+++ b/server/proxy/tcp.go
@@ -62,7 +62,7 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
}()
pxy.realBindPort = realBindPort
pxy.listeners = append(pxy.listeners, l)
- xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.LoadBalancer.Group)
+ xl.Infof("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.LoadBalancer.Group)
} else {
pxy.realBindPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
if err != nil {
@@ -79,7 +79,7 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
return
}
pxy.listeners = append(pxy.listeners, listener)
- xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
+ xl.Infof("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
}
pxy.cfg.RemotePort = pxy.realBindPort
diff --git a/server/proxy/tcpmux.go b/server/proxy/tcpmux.go
index 5b914c3983d..6e95b66b35b 100644
--- a/server/proxy/tcpmux.go
+++ b/server/proxy/tcpmux.go
@@ -65,7 +65,7 @@ func (pxy *TCPMuxProxy) httpConnectListen(
if err != nil {
return nil, err
}
- pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s], group [%s] routeByHTTPUser [%s]",
+ pxy.xl.Infof("tcpmux httpconnect multiplexer listens for host [%s], group [%s] routeByHTTPUser [%s]",
domain, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
pxy.listeners = append(pxy.listeners, l)
return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TCPMuxHTTPConnectPort)), nil
diff --git a/server/proxy/udp.go b/server/proxy/udp.go
index ea970818d8e..53a07d52676 100644
--- a/server/proxy/udp.go
+++ b/server/proxy/udp.go
@@ -97,10 +97,10 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
udpConn, errRet := net.ListenUDP("udp", addr)
if errRet != nil {
err = errRet
- xl.Warn("listen udp port error: %v", err)
+ xl.Warnf("listen udp port error: %v", err)
return
}
- xl.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
+ xl.Infof("udp proxy listen port [%d]", pxy.cfg.RemotePort)
pxy.udpConn = udpConn
pxy.sendCh = make(chan *msg.UDPPacket, 1024)
@@ -114,11 +114,11 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
rawMsg msg.Message
errRet error
)
- xl.Trace("loop waiting message from udp workConn")
+ xl.Tracef("loop waiting message from udp workConn")
// client will send heartbeat in workConn for keeping alive
_ = conn.SetReadDeadline(time.Now().Add(time.Duration(60) * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
- xl.Warn("read from workConn for udp error: %v", errRet)
+ xl.Warnf("read from workConn for udp error: %v", errRet)
_ = conn.Close()
// notify proxy to start a new work connection
// ignore error here, it means the proxy is closed
@@ -128,15 +128,15 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
return
}
if err := conn.SetReadDeadline(time.Time{}); err != nil {
- xl.Warn("set read deadline error: %v", err)
+ xl.Warnf("set read deadline error: %v", err)
}
switch m := rawMsg.(type) {
case *msg.Ping:
- xl.Trace("udp work conn get ping message")
+ xl.Tracef("udp work conn get ping message")
continue
case *msg.UDPPacket:
if errRet := errors.PanicToError(func() {
- xl.Trace("get udp message from workConn: %s", m.Content)
+ xl.Tracef("get udp message from workConn: %s", m.Content)
pxy.readCh <- m
metrics.Server.AddTrafficOut(
pxy.GetName(),
@@ -145,7 +145,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
)
}); errRet != nil {
conn.Close()
- xl.Info("reader goroutine for udp work connection closed")
+ xl.Infof("reader goroutine for udp work connection closed")
return
}
}
@@ -159,15 +159,15 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
select {
case udpMsg, ok := <-pxy.sendCh:
if !ok {
- xl.Info("sender goroutine for udp work connection closed")
+ xl.Infof("sender goroutine for udp work connection closed")
return
}
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
- xl.Info("sender goroutine for udp work connection closed: %v", errRet)
+ xl.Infof("sender goroutine for udp work connection closed: %v", errRet)
conn.Close()
return
}
- xl.Trace("send message to udp workConn: %s", udpMsg.Content)
+ xl.Tracef("send message to udp workConn: %s", udpMsg.Content)
metrics.Server.AddTrafficIn(
pxy.GetName(),
pxy.GetConfigurer().GetBaseConfig().Type,
@@ -175,7 +175,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
)
continue
case <-ctx.Done():
- xl.Info("sender goroutine for udp work connection closed")
+ xl.Infof("sender goroutine for udp work connection closed")
return
}
}
@@ -207,7 +207,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
if pxy.cfg.Transport.UseEncryption {
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Auth.Token))
if err != nil {
- xl.Error("create encryption stream error: %v", err)
+ xl.Errorf("create encryption stream error: %v", err)
workConn.Close()
continue
}
diff --git a/server/proxy/xtcp.go b/server/proxy/xtcp.go
index fe60c630c57..f69d0790d65 100644
--- a/server/proxy/xtcp.go
+++ b/server/proxy/xtcp.go
@@ -77,7 +77,7 @@ func (pxy *XTCPProxy) Run() (remoteAddr string, err error) {
}
errRet = msg.WriteMsg(workConn, m)
if errRet != nil {
- xl.Warn("write nat hole sid package error, %v", errRet)
+ xl.Warnf("write nat hole sid package error, %v", errRet)
}
workConn.Close()
}
diff --git a/server/service.go b/server/service.go
index c2410b06376..325f7f6cbf3 100644
--- a/server/service.go
+++ b/server/service.go
@@ -22,9 +22,11 @@ import (
"io"
"net"
"net/http"
+ "os"
"strconv"
"time"
+ "github.com/fatedier/golib/crypto"
"github.com/fatedier/golib/net/mux"
fmux "github.com/hashicorp/yamux"
quic "github.com/quic-go/quic-go"
@@ -59,6 +61,16 @@ const (
vhostReadWriteTimeout time.Duration = 30 * time.Second
)
+func init() {
+ crypto.DefaultSalt = "frp"
+ // Disable quic-go's receive buffer warning.
+ os.Setenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING", "true")
+ // Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
+ if os.Getenv("QUIC_GO_DISABLE_ECN") == "" {
+ os.Setenv("QUIC_GO_DISABLE_ECN", "true")
+ }
+}
+
// Server service
type Service struct {
// Dispatch connections to different handlers listen on same port
@@ -172,13 +184,13 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("create vhost tcpMuxer error, %v", err)
}
- log.Info("tcpmux httpconnect multiplexer listen on %s, passthough: %v", address, cfg.TCPMuxPassthrough)
+ log.Infof("tcpmux httpconnect multiplexer listen on %s, passthough: %v", address, cfg.TCPMuxPassthrough)
}
// Init all plugins
for _, p := range cfg.HTTPPlugins {
svr.pluginManager.Register(plugin.NewHTTPPluginOptions(p))
- log.Info("plugin [%s] has been registered", p.Name)
+ log.Infof("plugin [%s] has been registered", p.Name)
}
svr.rc.PluginManager = svr.pluginManager
@@ -222,7 +234,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
ln = svr.muxer.DefaultListener()
svr.listener = ln
- log.Info("frps tcp listen on %s", address)
+ log.Infof("frps tcp listen on %s", address)
// Listen for accepting connections from client using kcp protocol.
if cfg.KCPBindPort > 0 {
@@ -231,7 +243,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("listen on kcp udp address %s error: %v", address, err)
}
- log.Info("frps kcp listen on udp %s", address)
+ log.Infof("frps kcp listen on udp %s", address)
}
if cfg.QUICBindPort > 0 {
@@ -246,7 +258,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("listen on quic udp address %s error: %v", address, err)
}
- log.Info("frps quic listen on %s", address)
+ log.Infof("frps quic listen on %s", address)
}
if cfg.SSHTunnelGateway.BindPort > 0 {
@@ -255,7 +267,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
return nil, fmt.Errorf("create ssh gateway error: %v", err)
}
svr.sshTunnelGateway = sshGateway
- log.Info("frps sshTunnelGateway listen on port %d", cfg.SSHTunnelGateway.BindPort)
+ log.Infof("frps sshTunnelGateway listen on port %d", cfg.SSHTunnelGateway.BindPort)
}
// Listen for accepting connections from client using websocket protocol.
@@ -289,7 +301,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
go func() {
_ = server.Serve(l)
}()
- log.Info("http service listen on %s", address)
+ log.Infof("http service listen on %s", address)
}
// Create https vhost muxer.
@@ -303,7 +315,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
if err != nil {
return nil, fmt.Errorf("create server listener error, %v", err)
}
- log.Info("https service listen on %s", address)
+ log.Infof("https service listen on %s", address)
}
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
@@ -335,9 +347,9 @@ func (svr *Service) Run(ctx context.Context) {
// run dashboard web server.
if svr.webServer != nil {
go func() {
- log.Info("dashboard listen on %s", svr.webServer.Address())
+ log.Infof("dashboard listen on %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
- log.Warn("dashboard server exit with error: %v", err)
+ log.Warnf("dashboard server exit with error: %v", err)
}
}()
}
@@ -408,7 +420,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
_ = conn.SetReadDeadline(time.Now().Add(connReadTimeout))
if rawMsg, err = msg.ReadMsg(conn); err != nil {
- log.Trace("Failed to read message: %v", err)
+ log.Tracef("Failed to read message: %v", err)
conn.Close()
return
}
@@ -430,7 +442,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
// If login failed, send error message there.
// Otherwise send success message in control's work goroutine.
if err != nil {
- xl.Warn("register control error: %v", err)
+ xl.Warnf("register control error: %v", err)
_ = msg.WriteMsg(conn, &msg.LoginResp{
Version: version.Full(),
Error: util.GenerateResponseErrorString("register control error", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
@@ -443,7 +455,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
}
case *msg.NewVisitorConn:
if err = svr.RegisterVisitorConn(conn, m); err != nil {
- xl.Warn("register visitor conn error: %v", err)
+ xl.Warnf("register visitor conn error: %v", err)
_ = msg.WriteMsg(conn, &msg.NewVisitorConnResp{
ProxyName: m.ProxyName,
Error: util.GenerateResponseErrorString("register visitor conn error", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
@@ -456,7 +468,7 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
})
}
default:
- log.Warn("Error message type for the new connection [%s]", conn.RemoteAddr().String())
+ log.Warnf("Error message type for the new connection [%s]", conn.RemoteAddr().String())
conn.Close()
}
}
@@ -469,7 +481,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
for {
c, err := l.Accept()
if err != nil {
- log.Warn("Listener for incoming connections from client closed")
+ log.Warnf("Listener for incoming connections from client closed")
return
}
// inject xlog object into net.Conn context
@@ -479,17 +491,17 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
c = netpkg.NewContextConn(xlog.NewContext(ctx, xl), c)
if !internal {
- log.Trace("start check TLS connection...")
+ log.Tracef("start check TLS connection...")
originConn := c
forceTLS := svr.cfg.Transport.TLS.Force
var isTLS, custom bool
c, isTLS, custom, err = netpkg.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, forceTLS, connReadTimeout)
if err != nil {
- log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
+ log.Warnf("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
originConn.Close()
continue
}
- log.Trace("check TLS connection success, isTLS: %v custom: %v internal: %v", isTLS, custom, internal)
+ log.Tracef("check TLS connection success, isTLS: %v custom: %v internal: %v", isTLS, custom, internal)
}
// Start a new goroutine to handle connection.
@@ -501,7 +513,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
session, err := fmux.Server(frpConn, fmuxCfg)
if err != nil {
- log.Warn("Failed to create mux connection: %v", err)
+ log.Warnf("Failed to create mux connection: %v", err)
frpConn.Close()
return
}
@@ -509,7 +521,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
for {
stream, err := session.AcceptStream()
if err != nil {
- log.Debug("Accept new mux stream error: %v", err)
+ log.Debugf("Accept new mux stream error: %v", err)
session.Close()
return
}
@@ -527,7 +539,7 @@ func (svr *Service) HandleQUICListener(l *quic.Listener) {
for {
c, err := l.Accept(context.Background())
if err != nil {
- log.Warn("QUICListener for incoming connections from client closed")
+ log.Warnf("QUICListener for incoming connections from client closed")
return
}
// Start a new goroutine to handle connection.
@@ -535,7 +547,7 @@ func (svr *Service) HandleQUICListener(l *quic.Listener) {
for {
stream, err := frpConn.AcceptStream(context.Background())
if err != nil {
- log.Debug("Accept new quic mux stream error: %v", err)
+ log.Debugf("Accept new quic mux stream error: %v", err)
_ = frpConn.CloseWithError(0, "")
return
}
@@ -560,7 +572,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
xl := xlog.FromContextSafe(ctx)
xl.AppendPrefix(loginMsg.RunID)
ctx = xlog.NewContext(ctx, xl)
- xl.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
+ xl.Infof("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
// Check auth.
@@ -575,7 +587,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
// TODO(fatedier): use SessionContext
ctl, err := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, authVerifier, ctlConn, !internal, loginMsg, svr.cfg)
if err != nil {
- xl.Warn("create new controller error: %v", err)
+ xl.Warnf("create new controller error: %v", err)
// don't return detailed errors to client
return fmt.Errorf("unexpected error when creating new controller")
}
@@ -601,7 +613,7 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
xl := netpkg.NewLogFromConn(workConn)
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
if !exist {
- xl.Warn("No client control found for run id [%s]", newMsg.RunID)
+ xl.Warnf("No client control found for run id [%s]", newMsg.RunID)
return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID)
}
// server plugin hook
@@ -620,7 +632,7 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
err = ctl.authVerifier.VerifyNewWorkConn(newMsg)
}
if err != nil {
- xl.Warn("invalid NewWorkConn with run id [%s]", newMsg.RunID)
+ xl.Warnf("invalid NewWorkConn with run id [%s]", newMsg.RunID)
_ = msg.WriteMsg(workConn, &msg.StartWorkConn{
Error: util.GenerateResponseErrorString("invalid NewWorkConn", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
})
diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go
index 30887d2a7dc..994ac59deef 100644
--- a/test/e2e/e2e.go
+++ b/test/e2e/e2e.go
@@ -38,7 +38,7 @@ func RunE2ETests(t *testing.T) {
// Randomize specs as well as suites
suiteConfig.RandomizeAllSpecs = true
- log.Info("Starting e2e run %q on Ginkgo node %d of total %d",
+ log.Infof("Starting e2e run %q on Ginkgo node %d of total %d",
framework.RunID, suiteConfig.ParallelProcess, suiteConfig.ParallelTotal)
ginkgo.RunSpecs(t, "frp e2e suite", suiteConfig, reporterConfig)
}
diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go
index 27c36e0c809..e32f89f083f 100644
--- a/test/e2e/e2e_test.go
+++ b/test/e2e/e2e_test.go
@@ -34,7 +34,7 @@ func TestMain(m *testing.M) {
os.Exit(1)
}
- log.InitLog("console", framework.TestContext.LogLevel, 0, true)
+ log.InitLogger("console", framework.TestContext.LogLevel, 0, true)
os.Exit(m.Run())
}
diff --git a/test/e2e/framework/client.go b/test/e2e/framework/client.go
index 76614354a48..c13cee10a46 100644
--- a/test/e2e/framework/client.go
+++ b/test/e2e/framework/client.go
@@ -1,11 +1,9 @@
package framework
-type FRPClient struct {
- port int
-}
+import (
+ clientsdk "github.com/fatedier/frp/pkg/sdk/client"
+)
-func (f *Framework) FRPClient(port int) *FRPClient {
- return &FRPClient{
- port: port,
- }
+func (f *Framework) APIClientForFrpc(port int) *clientsdk.Client {
+ return clientsdk.New("127.0.0.1", port)
}
diff --git a/test/e2e/framework/expect.go b/test/e2e/framework/expect.go
index dab6da70ef0..3c357bb02cd 100644
--- a/test/e2e/framework/expect.go
+++ b/test/e2e/framework/expect.go
@@ -43,6 +43,10 @@ func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
}
+func ExpectContainSubstring(actual, substr string, explain ...interface{}) {
+ gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...)
+}
+
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go
index e0be5af14f9..527096cd1b4 100644
--- a/test/e2e/framework/framework.go
+++ b/test/e2e/framework/framework.go
@@ -217,7 +217,7 @@ func (f *Framework) RenderTemplates(templates []string) (outs []string, ports ma
}
for _, t := range templates {
- tmpl, err := template.New("").Parse(t)
+ tmpl, err := template.New("frp-e2e").Parse(t)
if err != nil {
return nil, nil, err
}
diff --git a/test/e2e/framework/process.go b/test/e2e/framework/process.go
index ae89e820078..0845914bf06 100644
--- a/test/e2e/framework/process.go
+++ b/test/e2e/framework/process.go
@@ -31,7 +31,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
ExpectNoError(err)
if TestContext.Debug {
- flog.Debug("[%s] %s", path, outs[i])
+ flog.Debugf("[%s] %s", path, outs[i])
}
p := process.NewWithEnvs(TestContext.FRPServerPath, []string{"-c", path}, f.osEnvs)
@@ -52,7 +52,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
ExpectNoError(err)
if TestContext.Debug {
- flog.Debug("[%s] %s", path, outs[index])
+ flog.Debugf("[%s] %s", path, outs[index])
}
p := process.NewWithEnvs(TestContext.FRPClientPath, []string{"-c", path}, f.osEnvs)
diff --git a/test/e2e/framework/request.go b/test/e2e/framework/request.go
index 6b75a2593ad..1fc67fe86ab 100644
--- a/test/e2e/framework/request.go
+++ b/test/e2e/framework/request.go
@@ -20,7 +20,7 @@ func ExpectResponseCode(code int) EnsureFunc {
if resp.Code == code {
return true
}
- flog.Warn("Expect code %d, but got %d", code, resp.Code)
+ flog.Warnf("Expect code %d, but got %d", code, resp.Code)
return false
}
}
@@ -111,14 +111,14 @@ func (e *RequestExpect) Ensure(fns ...EnsureFunc) {
if len(fns) == 0 {
if !bytes.Equal(e.expectResp, ret.Content) {
- flog.Trace("Response info: %+v", ret)
+ flog.Tracef("Response info: %+v", ret)
}
- ExpectEqualValuesWithOffset(1, ret.Content, e.expectResp, e.explain...)
+ ExpectEqualValuesWithOffset(1, string(ret.Content), string(e.expectResp), e.explain...)
} else {
for _, fn := range fns {
ok := fn(ret)
if !ok {
- flog.Trace("Response info: %+v", ret)
+ flog.Tracef("Response info: %+v", ret)
}
ExpectTrueWithOffset(1, ok, e.explain...)
}
diff --git a/test/e2e/legacy/basic/client.go b/test/e2e/legacy/basic/client.go
index d4862e529dc..98f675b823a 100644
--- a/test/e2e/legacy/basic/client.go
+++ b/test/e2e/legacy/basic/client.go
@@ -8,7 +8,6 @@ import (
"github.com/onsi/ginkgo/v2"
- clientsdk "github.com/fatedier/frp/pkg/sdk/client"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/request"
@@ -54,7 +53,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(p2Port).Ensure()
framework.NewRequestExpect(f).Port(p3Port).Ensure()
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
conf, err := client.GetConfig()
framework.ExpectNoError(err)
@@ -120,7 +119,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(testPort).Ensure()
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
err := client.Stop()
framework.ExpectNoError(err)
diff --git a/test/e2e/legacy/basic/server.go b/test/e2e/legacy/basic/server.go
index ca6717c639a..62bfd62ae71 100644
--- a/test/e2e/legacy/basic/server.go
+++ b/test/e2e/legacy/basic/server.go
@@ -7,7 +7,6 @@ import (
"github.com/onsi/ginkgo/v2"
- clientsdk "github.com/fatedier/frp/pkg/sdk/client"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port"
@@ -99,7 +98,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
f.RunProcesses([]string{serverConf}, []string{clientConf})
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
// tcp random port
status, err := client.GetProxyStatus("tcp")
diff --git a/test/e2e/legacy/features/monitor.go b/test/e2e/legacy/features/monitor.go
index 8968df9f01b..75aa183bb97 100644
--- a/test/e2e/legacy/features/monitor.go
+++ b/test/e2e/legacy/features/monitor.go
@@ -41,7 +41,7 @@ var _ = ginkgo.Describe("[Feature: Monitor]", func() {
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
}).Ensure(func(resp *request.Response) bool {
- log.Trace("prometheus metrics response: \n%s", resp.Content)
+ log.Tracef("prometheus metrics response: \n%s", resp.Content)
if resp.Code != 200 {
return false
}
diff --git a/test/e2e/legacy/features/real_ip.go b/test/e2e/legacy/features/real_ip.go
index 082df8c6260..f74c62d2d68 100644
--- a/test/e2e/legacy/features/real_ip.go
+++ b/test/e2e/legacy/features/real_ip.go
@@ -66,7 +66,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
- log.Error("read proxy protocol error: %v", err)
+ log.Errorf("read proxy protocol error: %v", err)
return
}
@@ -93,7 +93,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure(func(resp *request.Response) bool {
- log.Trace("ProxyProtocol get SourceAddr: %s", string(resp.Content))
+ log.Tracef("ProxyProtocol get SourceAddr: %s", string(resp.Content))
addr, err := net.ResolveTCPAddr("tcp", string(resp.Content))
if err != nil {
return false
@@ -121,7 +121,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
- log.Error("read proxy protocol error: %v", err)
+ log.Errorf("read proxy protocol error: %v", err)
return
}
srcAddrRecord = ppHeader.SourceAddr.String()
@@ -142,7 +142,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
r.HTTP().HTTPHost("normal.example.com")
}).Ensure(framework.ExpectResponseCode(404))
- log.Trace("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
+ log.Tracef("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
addr, err := net.ResolveTCPAddr("tcp", srcAddrRecord)
framework.ExpectNoError(err, srcAddrRecord)
framework.ExpectEqualValues("127.0.0.1", addr.IP.String())
diff --git a/test/e2e/pkg/plugin/plugin.go b/test/e2e/pkg/plugin/plugin.go
index 51de01d9383..b807a060a0d 100644
--- a/test/e2e/pkg/plugin/plugin.go
+++ b/test/e2e/pkg/plugin/plugin.go
@@ -26,7 +26,7 @@ func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler Handler, tl
w.WriteHeader(500)
return
}
- log.Trace("plugin request: %s", string(buf))
+ log.Tracef("plugin request: %s", string(buf))
err = json.Unmarshal(buf, &r)
if err != nil {
w.WriteHeader(500)
@@ -34,7 +34,7 @@ func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler Handler, tl
}
resp := handler(r)
buf, _ = json.Marshal(resp)
- log.Trace("plugin response: %s", string(buf))
+ log.Tracef("plugin response: %s", string(buf))
_, _ = w.Write(buf)
})),
)
diff --git a/test/e2e/v1/basic/client.go b/test/e2e/v1/basic/client.go
index b0b258db308..ef5e262f709 100644
--- a/test/e2e/v1/basic/client.go
+++ b/test/e2e/v1/basic/client.go
@@ -8,7 +8,6 @@ import (
"github.com/onsi/ginkgo/v2"
- clientsdk "github.com/fatedier/frp/pkg/sdk/client"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/request"
@@ -57,7 +56,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(p2Port).Ensure()
framework.NewRequestExpect(f).Port(p3Port).Ensure()
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
conf, err := client.GetConfig()
framework.ExpectNoError(err)
@@ -124,7 +123,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
framework.NewRequestExpect(f).Port(testPort).Ensure()
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
err := client.Stop()
framework.ExpectNoError(err)
diff --git a/test/e2e/v1/basic/config.go b/test/e2e/v1/basic/config.go
index 686b3c5c498..7dc3cedb3d9 100644
--- a/test/e2e/v1/basic/config.go
+++ b/test/e2e/v1/basic/config.go
@@ -38,6 +38,51 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
framework.NewRequestExpect(f).PortName(portName).Ensure()
})
+
+ ginkgo.It("Range ports mapping", func() {
+ serverConf := consts.DefaultServerConfig
+ clientConf := consts.DefaultClientConfig
+
+ adminPort := f.AllocPort()
+
+ localPortsRange := "13010-13012,13014"
+ remotePortsRange := "23010-23012,23014"
+ escapeTemplate := func(s string) string {
+ return "{{ `" + s + "` }}"
+ }
+ clientConf += fmt.Sprintf(`
+ webServer.port = %d
+
+ %s
+ [[proxies]]
+ name = "tcp-%s"
+ type = "tcp"
+ localPort = %s
+ remotePort = %s
+ %s
+ `, adminPort,
+ escapeTemplate(fmt.Sprintf(`{{- range $_, $v := parseNumberRangePair "%s" "%s" }}`, localPortsRange, remotePortsRange)),
+ escapeTemplate("{{ $v.First }}"),
+ escapeTemplate("{{ $v.First }}"),
+ escapeTemplate("{{ $v.Second }}"),
+ escapeTemplate("{{- end }}"),
+ )
+
+ f.RunProcesses([]string{serverConf}, []string{clientConf})
+
+ client := f.APIClientForFrpc(adminPort)
+ checkProxyFn := func(name string, localPort, remotePort int) {
+ status, err := client.GetProxyStatus(name)
+ framework.ExpectNoError(err)
+
+ framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort))
+ framework.ExpectContainSubstring(status.RemoteAddr, fmt.Sprintf(":%d", remotePort))
+ }
+ checkProxyFn("tcp-13010", 13010, 23010)
+ checkProxyFn("tcp-13011", 13011, 23011)
+ checkProxyFn("tcp-13012", 13012, 23012)
+ checkProxyFn("tcp-13014", 13014, 23014)
+ })
})
ginkgo.Describe("Includes", func() {
diff --git a/test/e2e/v1/basic/server.go b/test/e2e/v1/basic/server.go
index f2f4e0a5954..fe8af4d1108 100644
--- a/test/e2e/v1/basic/server.go
+++ b/test/e2e/v1/basic/server.go
@@ -7,7 +7,6 @@ import (
"github.com/onsi/ginkgo/v2"
- clientsdk "github.com/fatedier/frp/pkg/sdk/client"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
"github.com/fatedier/frp/test/e2e/pkg/port"
@@ -110,7 +109,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
f.RunProcesses([]string{serverConf}, []string{clientConf})
- client := clientsdk.New("127.0.0.1", adminPort)
+ client := f.APIClientForFrpc(adminPort)
// tcp random port
status, err := client.GetProxyStatus("tcp")
diff --git a/test/e2e/v1/features/monitor.go b/test/e2e/v1/features/monitor.go
index 5d4b8f9f908..fe92203a159 100644
--- a/test/e2e/v1/features/monitor.go
+++ b/test/e2e/v1/features/monitor.go
@@ -42,7 +42,7 @@ var _ = ginkgo.Describe("[Feature: Monitor]", func() {
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
}).Ensure(func(resp *request.Response) bool {
- log.Trace("prometheus metrics response: \n%s", resp.Content)
+ log.Tracef("prometheus metrics response: \n%s", resp.Content)
if resp.Code != 200 {
return false
}
diff --git a/test/e2e/v1/features/real_ip.go b/test/e2e/v1/features/real_ip.go
index 85338c62072..94508f7da9a 100644
--- a/test/e2e/v1/features/real_ip.go
+++ b/test/e2e/v1/features/real_ip.go
@@ -2,6 +2,7 @@ package features
import (
"bufio"
+ "crypto/tls"
"fmt"
"net"
"net/http"
@@ -9,6 +10,7 @@ import (
"github.com/onsi/ginkgo/v2"
pp "github.com/pires/go-proxyproto"
+ "github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/test/e2e/framework"
"github.com/fatedier/frp/test/e2e/framework/consts"
@@ -21,23 +23,24 @@ import (
var _ = ginkgo.Describe("[Feature: Real IP]", func() {
f := framework.NewDefaultFramework()
- ginkgo.It("HTTP X-Forwarded-For", func() {
- vhostHTTPPort := f.AllocPort()
- serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
+ ginkgo.Describe("HTTP X-forwarded-For", func() {
+ ginkgo.It("Client Without Header", func() {
+ vhostHTTPPort := f.AllocPort()
+ serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
vhostHTTPPort = %d
`, vhostHTTPPort)
- localPort := f.AllocPort()
- localServer := httpserver.New(
- httpserver.WithBindPort(localPort),
- httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- _, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
- })),
- )
- f.RunServer("", localServer)
-
- clientConf := consts.DefaultClientConfig
- clientConf += fmt.Sprintf(`
+ localPort := f.AllocPort()
+ localServer := httpserver.New(
+ httpserver.WithBindPort(localPort),
+ httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ _, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
+ })),
+ )
+ f.RunServer("", localServer)
+
+ clientConf := consts.DefaultClientConfig
+ clientConf += fmt.Sprintf(`
[[proxies]]
name = "test"
type = "http"
@@ -45,14 +48,131 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
customDomains = ["normal.example.com"]
`, localPort)
- f.RunProcesses([]string{serverConf}, []string{clientConf})
+ f.RunProcesses([]string{serverConf}, []string{clientConf})
- framework.NewRequestExpect(f).Port(vhostHTTPPort).
- RequestModify(func(r *request.Request) {
- r.HTTP().HTTPHost("normal.example.com")
- }).
- ExpectResp([]byte("127.0.0.1")).
- Ensure()
+ framework.NewRequestExpect(f).Port(vhostHTTPPort).
+ RequestModify(func(r *request.Request) {
+ r.HTTP().HTTPHost("normal.example.com")
+ }).
+ ExpectResp([]byte("127.0.0.1")).
+ Ensure()
+ })
+
+ ginkgo.It("Client With Header", func() {
+ vhostHTTPPort := f.AllocPort()
+ serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
+ vhostHTTPPort = %d
+ `, vhostHTTPPort)
+
+ localPort := f.AllocPort()
+ localServer := httpserver.New(
+ httpserver.WithBindPort(localPort),
+ httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ _, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
+ })),
+ )
+ f.RunServer("", localServer)
+
+ clientConf := consts.DefaultClientConfig
+ clientConf += fmt.Sprintf(`
+ [[proxies]]
+ name = "test"
+ type = "http"
+ localPort = %d
+ customDomains = ["normal.example.com"]
+ `, localPort)
+
+ f.RunProcesses([]string{serverConf}, []string{clientConf})
+
+ framework.NewRequestExpect(f).Port(vhostHTTPPort).
+ RequestModify(func(r *request.Request) {
+ r.HTTP().HTTPHost("normal.example.com")
+ r.HTTP().HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2"})
+ }).
+ ExpectResp([]byte("2.2.2.2, 127.0.0.1")).
+ Ensure()
+ })
+
+ ginkgo.It("http2https plugin", func() {
+ vhostHTTPPort := f.AllocPort()
+ serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
+ vhostHTTPPort = %d
+ `, vhostHTTPPort)
+
+ localPort := f.AllocPort()
+
+ clientConf := consts.DefaultClientConfig
+ clientConf += fmt.Sprintf(`
+ [[proxies]]
+ name = "test"
+ type = "http"
+ customDomains = ["normal.example.com"]
+ [proxies.plugin]
+ type = "http2https"
+ localAddr = "127.0.0.1:%d"
+ `, localPort)
+
+ f.RunProcesses([]string{serverConf}, []string{clientConf})
+
+ tlsConfig, err := transport.NewServerTLSConfig("", "", "")
+ framework.ExpectNoError(err)
+
+ localServer := httpserver.New(
+ httpserver.WithBindPort(localPort),
+ httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ _, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
+ })),
+ httpserver.WithTLSConfig(tlsConfig),
+ )
+ f.RunServer("", localServer)
+
+ framework.NewRequestExpect(f).Port(vhostHTTPPort).
+ RequestModify(func(r *request.Request) {
+ r.HTTP().HTTPHost("normal.example.com")
+ r.HTTP().HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2, 3.3.3.3"})
+ }).
+ ExpectResp([]byte("2.2.2.2, 3.3.3.3, 127.0.0.1")).
+ Ensure()
+ })
+
+ ginkgo.It("https2http plugin", func() {
+ vhostHTTPSPort := f.AllocPort()
+ serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
+ vhostHTTPSPort = %d
+ `, vhostHTTPSPort)
+
+ localPort := f.AllocPort()
+
+ clientConf := consts.DefaultClientConfig
+ clientConf += fmt.Sprintf(`
+ [[proxies]]
+ name = "test"
+ type = "https"
+ customDomains = ["normal.example.com"]
+ [proxies.plugin]
+ type = "https2http"
+ localAddr = "127.0.0.1:%d"
+ `, localPort)
+
+ f.RunProcesses([]string{serverConf}, []string{clientConf})
+
+ localServer := httpserver.New(
+ httpserver.WithBindPort(localPort),
+ httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ _, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
+ })),
+ )
+ f.RunServer("", localServer)
+
+ framework.NewRequestExpect(f).Port(vhostHTTPSPort).
+ RequestModify(func(r *request.Request) {
+ r.HTTPS().HTTPHost("normal.example.com").
+ HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2"}).
+ TLSConfig(&tls.Config{ServerName: "normal.example.com", InsecureSkipVerify: true})
+ }).
+ ExpectResp([]byte("2.2.2.2, 127.0.0.1")).
+ Ensure()
+ })
})
ginkgo.Describe("Proxy Protocol", func() {
@@ -67,7 +187,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
- log.Error("read proxy protocol error: %v", err)
+ log.Errorf("read proxy protocol error: %v", err)
return
}
@@ -95,7 +215,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
f.RunProcesses([]string{serverConf}, []string{clientConf})
framework.NewRequestExpect(f).Port(remotePort).Ensure(func(resp *request.Response) bool {
- log.Trace("ProxyProtocol get SourceAddr: %s", string(resp.Content))
+ log.Tracef("ProxyProtocol get SourceAddr: %s", string(resp.Content))
addr, err := net.ResolveTCPAddr("tcp", string(resp.Content))
if err != nil {
return false
@@ -123,7 +243,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
rd := bufio.NewReader(c)
ppHeader, err := pp.Read(rd)
if err != nil {
- log.Error("read proxy protocol error: %v", err)
+ log.Errorf("read proxy protocol error: %v", err)
return
}
srcAddrRecord = ppHeader.SourceAddr.String()
@@ -145,7 +265,7 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
r.HTTP().HTTPHost("normal.example.com")
}).Ensure(framework.ExpectResponseCode(404))
- log.Trace("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
+ log.Tracef("ProxyProtocol get SourceAddr: %s", srcAddrRecord)
addr, err := net.ResolveTCPAddr("tcp", srcAddrRecord)
framework.ExpectNoError(err, srcAddrRecord)
framework.ExpectEqualValues("127.0.0.1", addr.IP.String())
diff --git a/test/e2e/v1/plugin/client.go b/test/e2e/v1/plugin/client.go
index f544570ae7e..476adb89e09 100644
--- a/test/e2e/v1/plugin/client.go
+++ b/test/e2e/v1/plugin/client.go
@@ -30,10 +30,11 @@ var _ = ginkgo.Describe("[Feature: Client-Plugins]", func() {
name = "%s"
type = "tcp"
remotePort = {{ .%s }}
+ `+extra, proxyName, portName) + fmt.Sprintf(`
[proxies.plugin]
type = "unix_domain_socket"
unixPath = "{{ .%s }}"
- `+extra, proxyName, portName, framework.UDSEchoServerAddr)
+ `, framework.UDSEchoServerAddr)
}
tests := []struct {
diff --git a/web/frps/src/components/ProxyView.vue b/web/frps/src/components/ProxyView.vue
index 2811a30f5e4..97a6e071f51 100644
--- a/web/frps/src/components/ProxyView.vue
+++ b/web/frps/src/components/ProxyView.vue
@@ -111,7 +111,7 @@ const formatTrafficOut = (row: BaseProxy, _: TableColumnCtx) => {
}
const clearOfflineProxies = () => {
- fetch('/api/proxies?status=offline', {
+ fetch('../api/proxies?status=offline', {
method: 'DELETE',
credentials: 'include',
})