@@ -30,39 +30,65 @@ void addCORSHeaders(HttpRequest request) {
3030}
3131
3232void main (List <String > arguments) async {
33- if (File .fromUri (Uri .file ('.env' )).existsSync ())
34- dotenv.load ();
33+ String dotEnvFile = arguments.firstOrNull ?? '.env' ;
34+ if (File .fromUri (Uri .file (dotEnvFile)).existsSync ())
35+ dotenv.load (dotEnvFile);
3536
37+ // Local server
3638 final InternetAddress localIp = InternetAddress .tryParse (dotenv.env['LOCAL_BIND_IP' ] ?? '' ) ?? InternetAddress .loopbackIPv4;
3739 final int localPort = int .tryParse (dotenv.env['LOCAL_PORT' ] ?? '' ) ?? 8080 ;
40+ // Local auth
41+ final String ? localUsername = dotenv.env['LOCAL_USERNAME' ];
42+ final String ? localPassword = dotenv.env['LOCAL_PASSWORD' ];
43+ final String ? localBasicAuth = (localUsername != null && localPassword != null )
44+ ? 'Basic ${base64Encode (utf8 .encode ('$localUsername :$localPassword ' ))}'
45+ : null ;
46+ final String localBaseUrl = 'http://${localIp .host }:$localPort ' ;
47+
48+ // Remote server
3849 final String serverScheme = dotenv.env['SERVER_SCHEME' ] ?? 'https' ;
3950 final String serverHost = dotenv.env['SERVER_HOST' ] ?? 'example.com' ;
4051 final int serverPort = int .tryParse (dotenv.env['SERVER_PORT' ] ?? (serverScheme == 'https' ? '443' : '' )) ?? 80 ;
52+ // Server auth
4153 final String ? serverUsername = dotenv.env['SERVER_USERNAME' ];
4254 final String ? serverPassword = dotenv.env['SERVER_PASSWORD' ];
4355 final String ? serverBasicAuth = (serverUsername != null && serverPassword != null )
4456 ? 'Basic ${base64Encode (utf8 .encode ('$serverUsername :$serverPassword ' ))}'
4557 : null ;
4658 final String serverBaseUrl = '$serverScheme ://$serverHost ${![ 'http' , 'https' , ].contains (serverScheme ) ? serverPort : '' }' ;
47- final String ? localUsername = dotenv.env['LOCAL_USERNAME' ];
48- final String ? localPassword = dotenv.env['LOCAL_PASSWORD' ];
49- final String ? localBasicAuth = (localUsername != null && localPassword != null )
50- ? 'Basic ${base64Encode (utf8 .encode ('$localUsername :$localPassword ' ))}'
59+
60+ final Uri ? httpProxy = Uri .tryParse (dotenv.env['HTTP_PROXY' ] ?? '::Not valid URI::' );
61+ final RegExpMatch ? match = httpProxy != null
62+ ? RegExp (r'^(?<username>.+?):(?<password>.+?)$' )
63+ .firstMatch (httpProxy.userInfo)
64+ : null ;
65+ final String ? proxyUsername = match? .namedGroup ('username' );
66+ final String ? proxyPassword = match? .namedGroup ('password' );
67+ final HttpClientBasicCredentials ? httpProxyCredentials = (proxyUsername != null && proxyPassword != null )
68+ ? HttpClientBasicCredentials (proxyUsername, proxyPassword)
5169 : null ;
52- final String localBaseUrl = 'http://${localIp .host }:$localPort ' ;
5370
5471 stdout.write ('Starting mirror server $localBaseUrl -> $serverBaseUrl ' );
5572 if (localBasicAuth != null )
5673 stdout.write (' [Local auth]' );
5774 if (serverBasicAuth != null )
5875 stdout.write (' [Remote auth auto-fill]' );
76+ if (httpProxy != null ) {
77+ stdout.write (' [Through HTTP proxy]' );
78+ if (httpProxy.scheme != 'http' ) {
79+ stdout.writeln (' [Error]' );
80+ stderr.writeln ('Proxy URI must be valid.' );
81+ return ;
82+ }
83+ }
5984
6085 late final HttpServer server;
6186 try {
6287 server = await HttpServer .bind (localIp, localPort);
6388 } catch (error) {
6489 stdout.writeln (' [Error]' );
65- stderr.writeln ('Error unable to bind server.' );
90+ stderr.writeln ('Error unable to bind server:' );
91+ stderr.writeln (error);
6692 return ;
6793 }
6894 stdout.writeln (' [Done]' );
@@ -71,12 +97,28 @@ void main(List<String> arguments) async {
7197 return trustedCert.contains (String .fromCharCodes (cert.sha1));
7298 };
7399
100+ // HTTP proxy
101+ if (httpProxy != null ) {
102+ if (httpProxyCredentials != null ) {
103+ client.addProxyCredentials (
104+ httpProxy.host,
105+ httpProxy.port,
106+ 'Basic' ,
107+ httpProxyCredentials
108+ );
109+ }
110+ client.findProxy = (uri) => 'PROXY ${httpProxy .host }:${httpProxy .port }' ;
111+ }
112+
74113 server.listen ((HttpRequest request) {
75114 addCORSHeaders (request);
76115 final HttpResponse response = request.response;
77116
78117 // preflight
79- if (request.method == 'OPTIONS' && request.headers['access-control-request-method' ] != null ) {
118+ if (
119+ request.method == 'OPTIONS' &&
120+ request.headers[HttpHeaders .accessControlRequestMethodHeader] != null
121+ ) {
80122 response
81123 ..contentLength = 0
82124 ..statusCode = HttpStatus .ok
@@ -85,11 +127,11 @@ void main(List<String> arguments) async {
85127 }
86128
87129 if (localBasicAuth != null ) {
88- final String ? _userAuth = request.headers['authorization' ]? .singleOrNull;
130+ final String ? _userAuth = request.headers[HttpHeaders .authorizationHeader ]? .singleOrNull;
89131 if (_userAuth == null || _userAuth != localBasicAuth) {
90132 response
91133 ..statusCode = HttpStatus .unauthorized
92- ..headers.add ('WWW-Authenticate' , 'Basic realm=Protected' )
134+ ..headers.add (HttpHeaders .wwwAuthenticateHeader , 'Basic realm=Protected' )
93135 ..headers.contentType = ContentType .text
94136 ..write ('PROXY///ERROR///UNAUTHORIZED' )
95137 ..close ();
@@ -111,12 +153,13 @@ void main(List<String> arguments) async {
111153 .openUrl (request.method, targetUri)
112154 .then ((HttpClientRequest proxyRequest) async {
113155 if (serverBasicAuth != null )
114- proxyRequest.headers.add ('Authorization' , serverBasicAuth);
115- request.headers.forEach ((name, values) {
156+ proxyRequest.headers.add (HttpHeaders .authorizationHeader , serverBasicAuth);
157+ request.headers.forEach ((String name, List < String > values) {
116158 if (! [
117- 'host' ,
118- ].contains (name))
119- if (name == 'referer' )
159+ // Headers to skip
160+ HttpHeaders .hostHeader,
161+ ].contains (name)) {
162+ if (name == HttpHeaders .refererHeader)
120163 proxyRequest.headers.add (
121164 name,
122165 values.map (
@@ -125,6 +168,7 @@ void main(List<String> arguments) async {
125168 );
126169 else
127170 proxyRequest.headers.add (name, values);
171+ }
128172 });
129173 if (request.contentLength > 0 )
130174 await proxyRequest.addStream (request);
@@ -134,9 +178,9 @@ void main(List<String> arguments) async {
134178 stdout.write (' [${proxyResponse .statusCode }]' );
135179 proxyResponse.headers.forEach ((name, values) {
136180 if (! [
137- 'connection' ,
138- 'content-length' ,
139- 'content-encoding' ,
181+ HttpHeaders .connectionHeader ,
182+ HttpHeaders .contentLengthHeader ,
183+ HttpHeaders .contentEncodingHeader ,
140184 ].contains (name))
141185 response.headers.add (name, values);
142186 });
0 commit comments