33import com .github .balloonupdate .mcpatch .client .config .AppConfig ;
44import com .github .balloonupdate .mcpatch .client .data .Range ;
55import com .github .balloonupdate .mcpatch .client .exceptions .McpatchBusinessException ;
6+ import com .github .balloonupdate .mcpatch .client .logging .Log ;
67import 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 ;
911
12+ import java .io .IOException ;
13+ import java .net .ConnectException ;
14+ import java .net .SocketException ;
15+ import java .net .SocketTimeoutException ;
1016import java .nio .file .Path ;
1117import java .util .HashMap ;
18+ import java .util .Map ;
19+ import java .util .concurrent .TimeUnit ;
1220
1321public class AlistProtocol implements UpdatingServer {
1422 /**
@@ -32,7 +40,7 @@ public class AlistProtocol implements UpdatingServer {
3240 OkHttpClient client ;
3341
3442 /**
35- * 原始路径缓存 path -> raw_url
43+ * 下载链接缓存 path -> raw_url
3644 */
3745 HashMap <String , String > cache = new HashMap <>();
3846
@@ -41,10 +49,33 @@ public AlistProtocol(int number, String url, AppConfig config) {
4149 if (!url .endsWith ("/" )) {
4250 url = url + "/" ;
4351 }
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 ();
4473 }
4574
4675 @ Override
4776 public String requestText (String path , Range range , String desc ) throws McpatchBusinessException {
77+ String rawPath = cache .get (path );
78+
4879 return "" ;
4980 }
5081
@@ -67,8 +98,135 @@ public void close() throws Exception {
6798 * @throws McpatchBusinessException 请求失败时
6899 */
69100 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 );
71229
72- throw new RuntimeException ( "还没有实现这个方法" ) ;
230+ return rawUrl ;
73231 }
74232}
0 commit comments