@@ -12,6 +12,7 @@ use anyhow::Error;
12
12
use http_range_header:: {
13
13
parse_range_header, EndPosition , StartPosition , SyntacticallyCorrectRange ,
14
14
} ;
15
+ use log:: { debug, warn} ;
15
16
use nostr:: Event ;
16
17
use rocket:: fs:: NamedFile ;
17
18
use rocket:: http:: { ContentType , Header , Status } ;
@@ -100,13 +101,27 @@ impl Nip94Event {
100
101
}
101
102
}
102
103
104
+ /// Range request handler over file handle
103
105
struct RangeBody {
104
- pub file : File ,
105
- pub file_size : u64 ,
106
- pub ranges : Vec < SyntacticallyCorrectRange > ,
107
-
106
+ file : File ,
107
+ file_size : u64 ,
108
+ ranges : Vec < SyntacticallyCorrectRange > ,
108
109
current_range_index : usize ,
109
110
current_offset : u64 ,
111
+ poll_complete : bool ,
112
+ }
113
+
114
+ impl RangeBody {
115
+ pub fn new ( file : File , file_size : u64 , ranges : Vec < SyntacticallyCorrectRange > ) -> Self {
116
+ Self {
117
+ file,
118
+ file_size,
119
+ ranges,
120
+ current_offset : 0 ,
121
+ current_range_index : 0 ,
122
+ poll_complete : false ,
123
+ }
124
+ }
110
125
}
111
126
112
127
impl AsyncRead for RangeBody {
@@ -138,23 +153,32 @@ impl AsyncRead for RangeBody {
138
153
return self . poll_read ( cx, buf) ;
139
154
}
140
155
141
- let pinned = pin ! ( & mut self . file) ;
142
- pinned. start_seek ( SeekFrom :: Start ( range_start) ) ?;
156
+ if !self . poll_complete {
157
+ // start seeking to our read position
158
+ let pinned = pin ! ( & mut self . file) ;
159
+ pinned. start_seek ( SeekFrom :: Start ( range_start) ) ?;
160
+ self . poll_complete = true ;
161
+ }
143
162
144
- let pinned = pin ! ( & mut self . file) ;
145
- match pinned. poll_complete ( cx) {
146
- Poll :: Ready ( Ok ( _) ) => { }
147
- Poll :: Ready ( Err ( e) ) => return Poll :: Ready ( Err ( e) ) ,
148
- Poll :: Pending => return Poll :: Pending ,
163
+ if self . poll_complete {
164
+ let pinned = pin ! ( & mut self . file) ;
165
+ match pinned. poll_complete ( cx) {
166
+ Poll :: Ready ( Ok ( _) ) => {
167
+ self . poll_complete = false ;
168
+ }
169
+ Poll :: Ready ( Err ( e) ) => return Poll :: Ready ( Err ( e) ) ,
170
+ Poll :: Pending => return Poll :: Pending ,
171
+ }
149
172
}
150
173
151
174
// Read data from the file
152
175
let pinned = pin ! ( & mut self . file) ;
153
- let n = pinned. poll_read ( cx, & mut buf. take ( bytes_to_read as usize ) ) ;
176
+ let n = pinned. poll_read ( cx, buf) ;
154
177
if let Poll :: Ready ( Ok ( ( ) ) ) = n {
155
178
self . current_offset += bytes_to_read;
156
179
Poll :: Ready ( Ok ( ( ) ) )
157
180
} else {
181
+ self . poll_complete = true ;
158
182
Poll :: Pending
159
183
}
160
184
}
@@ -170,30 +194,51 @@ impl<'r> Responder<'r, 'static> for FilePayload {
170
194
response. set_header ( Header :: new ( "accept-ranges" , "bytes" ) ) ;
171
195
if let Some ( r) = request. headers ( ) . get ( "range" ) . next ( ) {
172
196
if let Ok ( ranges) = parse_range_header ( r) {
173
- let r_body = RangeBody {
174
- file_size : self . info . size , // TODO: handle filesize mismatch
175
- file : self . file ,
176
- ranges : ranges. ranges ,
177
- current_range_index : 0 ,
178
- current_offset : 0 ,
179
- } ;
180
- response. set_streamed_body ( Box :: pin ( r_body) ) ;
197
+ if ranges. ranges . len ( ) > 1 {
198
+ warn ! ( "Multipart ranges are not supported, fallback to non-range request" ) ;
199
+ response. set_streamed_body ( self . file ) ;
200
+ } else {
201
+ let single_range = ranges. ranges . first ( ) . unwrap ( ) ;
202
+ let range_start = match single_range. start {
203
+ StartPosition :: Index ( i) => i,
204
+ StartPosition :: FromLast ( i) => self . info . size - i,
205
+ } ;
206
+ let range_end = match single_range. end {
207
+ EndPosition :: Index ( i) => i,
208
+ EndPosition :: LastByte => self . info . size ,
209
+ } ;
210
+ debug ! ( "Range: {:?} {:?}" , range_start..range_end, single_range) ;
211
+ let r_len = range_end - range_start;
212
+ let r_body = RangeBody :: new ( self . file , self . info . size , ranges. ranges ) ;
213
+
214
+ response. set_status ( Status :: PartialContent ) ;
215
+ response. set_header ( Header :: new ( "content-length" , r_len. to_string ( ) ) ) ;
216
+ response. set_header ( Header :: new (
217
+ "content-range" ,
218
+ format ! ( "bytes {}-{}/{}" , range_start, range_end, self . info. size) ,
219
+ ) ) ;
220
+ response. set_streamed_body ( Box :: pin ( r_body) ) ;
221
+ }
181
222
}
182
223
} else {
183
224
response. set_streamed_body ( self . file ) ;
184
225
}
185
226
}
186
227
#[ cfg( not( feature = "ranges" ) ) ]
187
- response. set_streamed_body ( self . file ) ;
188
- response. set_header ( Header :: new ( "content-length" , self . info . size . to_string ( ) ) ) ;
228
+ {
229
+ response. set_streamed_body ( self . file ) ;
230
+ response. set_header ( Header :: new ( "content-length" , self . info . size . to_string ( ) ) ) ;
231
+ }
189
232
190
233
if let Ok ( ct) = ContentType :: from_str ( & self . info . mime_type ) {
191
234
response. set_header ( ct) ;
192
235
}
193
- response. set_header ( Header :: new (
194
- "content-disposition" ,
195
- format ! ( "inline; filename=\" {}\" " , self . info. name) ,
196
- ) ) ;
236
+ if !self . info . name . is_empty ( ) {
237
+ response. set_header ( Header :: new (
238
+ "content-disposition" ,
239
+ format ! ( "inline; filename=\" {}\" " , self . info. name) ,
240
+ ) ) ;
241
+ }
197
242
Ok ( response)
198
243
}
199
244
}
@@ -247,7 +292,7 @@ async fn delete_file(
247
292
#[ rocket:: get( "/" ) ]
248
293
pub async fn root ( ) -> Result < NamedFile , Status > {
249
294
#[ cfg( debug_assertions) ]
250
- let index = "./ui_src/dist/ index.html" ;
295
+ let index = "./index.html" ;
251
296
#[ cfg( not( debug_assertions) ) ]
252
297
let index = "./ui/index.html" ;
253
298
if let Ok ( f) = NamedFile :: open ( index) . await {
0 commit comments