3
3
import com .github .balloonupdate .mcpatch .client .config .AppConfig ;
4
4
import com .github .balloonupdate .mcpatch .client .data .Range ;
5
5
import com .github .balloonupdate .mcpatch .client .exceptions .McpatchBusinessException ;
6
+ import com .github .balloonupdate .mcpatch .client .logging .Log ;
6
7
import com .github .balloonupdate .mcpatch .client .network .UpdatingServer ;
7
- import okhttp3 .OkHttpClient ;
8
- import okhttp3 .Response ;
8
+ import com .github .balloonupdate .mcpatch .client .utils .RuntimeAssert ;
9
+ import okhttp3 .*;
10
+ import org .json .JSONObject ;
9
11
12
+ import java .io .IOException ;
13
+ import java .net .ConnectException ;
14
+ import java .net .SocketException ;
15
+ import java .net .SocketTimeoutException ;
10
16
import java .nio .file .Path ;
11
17
import java .util .HashMap ;
18
+ import java .util .Map ;
19
+ import java .util .concurrent .TimeUnit ;
12
20
13
21
public class AlistProtocol implements UpdatingServer {
14
22
/**
@@ -32,7 +40,7 @@ public class AlistProtocol implements UpdatingServer {
32
40
OkHttpClient client ;
33
41
34
42
/**
35
- * 原始路径缓存 path -> raw_url
43
+ * 下载链接缓存 path -> raw_url
36
44
*/
37
45
HashMap <String , String > cache = new HashMap <>();
38
46
@@ -41,10 +49,33 @@ public AlistProtocol(int number, String url, AppConfig config) {
41
49
if (!url .endsWith ("/" )) {
42
50
url = url + "/" ;
43
51
}
52
+
53
+ // 去掉开头的 alist:// ,留下后面的部分
54
+ url = url .substring ("alist://" .length ());
55
+
56
+ baseUrl = url ;
57
+
58
+ // 创建 HTTP 客户端对象
59
+ OkHttpClient .Builder builder = new OkHttpClient .Builder ();
60
+
61
+ // 忽略证书
62
+ if (config .ignoreSSLCertificate ) {
63
+ HttpProtocol .IgnoreSSLCert ignore = new HttpProtocol .IgnoreSSLCert ();
64
+
65
+ builder .sslSocketFactory (ignore .context .getSocketFactory (), ignore .trustManager );
66
+ }
67
+
68
+ client = builder
69
+ .connectTimeout (config .httpTimeout , TimeUnit .MILLISECONDS )
70
+ .readTimeout (config .httpTimeout , TimeUnit .MILLISECONDS )
71
+ .writeTimeout (config .httpTimeout , TimeUnit .MILLISECONDS )
72
+ .build ();
44
73
}
45
74
46
75
@ Override
47
76
public String requestText (String path , Range range , String desc ) throws McpatchBusinessException {
77
+ String rawPath = cache .get (path );
78
+
48
79
return "" ;
49
80
}
50
81
@@ -67,8 +98,135 @@ public void close() throws Exception {
67
98
* @throws McpatchBusinessException 请求失败时
68
99
*/
69
100
Response request (String path , Range range , String desc ) throws McpatchBusinessException {
70
- String rawPath = cache .get (path );
101
+ // 检查输入参数,start不能大于end
102
+ boolean partial_file = range .start > 0 || range .end > 0 ;
103
+
104
+ if (partial_file ) {
105
+ RuntimeAssert .isTrue (range .end >= range .start );
106
+ }
107
+
108
+ // 拼接 URL
109
+ String url = baseUrl + path ;
110
+
111
+ // 构建请求
112
+ Request req = buildRequest (url , range , null , null );
113
+
114
+ try {
115
+ Response rsp = client .newCall (req ).execute ();
116
+ int code = rsp .code ();
117
+
118
+ // 检查状态码
119
+ if ((!partial_file && (code < 200 || code >= 300 )) || (partial_file && code != 206 )) {
120
+ // 如果状态码不对,就考虑输出响应体内容,因为通常会包含一些服务端返回的错误信息,对排查问题很有帮助
121
+ String body = rsp .peekBody (300 ).string ();
122
+
123
+ String content = String .format ("服务器(%d)返回了 %d 而不是206: %s (%s)\n %s" , number , code , path , desc , body );
124
+
125
+ throw new McpatchBusinessException (content );
126
+ }
127
+
128
+ // 检查content-length
129
+ long len = rsp .body ().contentLength ();
130
+
131
+ if (len == -1 ) {
132
+ throw new McpatchBusinessException (String .format ("服务器(%d)没有返回 content-length 头:%s (%s)" , number , path , desc ));
133
+ }
134
+
135
+ if (range .len () > 0 && len != range .len ()) {
136
+ String text = String .format ("服务器(%d)返回的 content-length 头 %d 不等于 %d: %s" , number , len , range .len (), path );
137
+
138
+ throw new McpatchBusinessException (text );
139
+ }
140
+
141
+ return rsp ;
142
+ } catch (ConnectException e ) {
143
+ throw new McpatchBusinessException ("连接被拒绝,请检查网络。" + url , e );
144
+ } catch (SocketException e ) {
145
+ throw new McpatchBusinessException ("连接中断,请检查网络。" + url , e );
146
+ } catch (SocketTimeoutException e ) {
147
+ throw new McpatchBusinessException ("连接超市,请检查网络。" + url , e );
148
+ } catch (Exception e ) {
149
+ throw new McpatchBusinessException (e );
150
+ }
151
+ }
152
+
153
+ /**
154
+ * 构建一个请求
155
+ * @param url 请求的 url
156
+ * @param range 请求的范围
157
+ * @param headers 额外的 headers
158
+ * @return 响应
159
+ */
160
+ private Request buildRequest (String url , Range range , RequestBody body , Map <String , String > headers ) {
161
+ Request .Builder req = new Request .Builder ().url (url );
162
+
163
+ // 添加json响应请求
164
+ req .addHeader ("Content-Type" , "application/json" );
165
+
166
+ // 只请求部分文件
167
+ if (range .len () > 0 ) {
168
+ req .addHeader ("Range" , String .format ("bytes=%d-%d" , range .start , range .end - 1 ));
169
+ }
170
+
171
+ // 添加额外headers
172
+ if (headers != null ) {
173
+ for (Map .Entry <String , String > e : headers .entrySet ())
174
+ req .addHeader (e .getKey (), e .getValue ());
175
+ }
176
+
177
+ // 添加自定义headers
178
+ for (Map .Entry <String , String > e : config .httpHeaders .entrySet ()) {
179
+ req .addHeader (e .getKey (), e .getValue ());
180
+ }
181
+
182
+ // 添加body
183
+ if (body != null ) {
184
+ req .setBody$okhttp (body );
185
+ }
186
+
187
+ return req .build ();
188
+ }
189
+
190
+ /**
191
+ * 获取文件的原始链接
192
+ */
193
+ String fetchDownloadLink (String filename ) throws IOException , McpatchBusinessException {
194
+ if (cache .containsKey (filename ))
195
+ return cache .get (filename );
196
+
197
+ String path = baseUrl + filename ;
198
+
199
+ int split = path .indexOf ("/" , "https://" .length ());
200
+
201
+ path = path .substring (split );
202
+
203
+ Log .info ("split: " + path );
204
+
205
+
206
+ String url = baseUrl + "api/fs/get" ;
207
+
208
+ String bodyText = String .format ("\" path\" : \" %s\" ,\" password\" : \" \" " , path );
209
+ RequestBody body = RequestBody .create (bodyText , MediaType .get ("text/json" ));
210
+
211
+ Request req = buildRequest (url , Range .Empty (), body , null );
212
+
213
+ Response rsp = client .newCall (req ).execute ();
214
+
215
+ if (!rsp .isSuccessful ()) {
216
+ // 如果状态码不对,就考虑输出响应体内容,因为通常会包含一些服务端返回的错误信息,对排查问题很有帮助
217
+ String b = rsp .peekBody (300 ).string ();
218
+
219
+ String content = String .format ("服务器(%d)返回了 %d 而不是206: %s (%s)\n %s" , number , rsp .code (), path , "请求原始下载链接" , b );
220
+
221
+ throw new McpatchBusinessException (content );
222
+ }
223
+
224
+ JSONObject json = new JSONObject (rsp .body ());
225
+
226
+ String rawUrl = (String ) json .query ("/data/raw_url" );
227
+
228
+ cache .put (path , rawUrl );
71
229
72
- throw new RuntimeException ( "还没有实现这个方法" ) ;
230
+ return rawUrl ;
73
231
}
74
232
}
0 commit comments