11use std:: marker:: MarkerTrait ;
22use std:: io:: { self , Read , Seek , Cursor , Write , SeekFrom } ;
33use std;
4+ use std:: fmt;
5+ use std:: str:: FromStr ;
46
57use mime:: { Mime , TopLevel , SubLevel , Attr , Value } ;
68use oauth2;
79use oauth2:: TokenType ;
810use hyper;
9- use hyper:: header:: { ContentType , ContentLength , Headers , UserAgent , Authorization } ;
11+ use hyper:: header:: { ContentType , ContentLength , Headers , UserAgent , Authorization , Header ,
12+ HeaderFormat } ;
1013use hyper:: http:: LINE_ENDING ;
1114use hyper:: method:: Method ;
1215
@@ -361,6 +364,107 @@ impl_header!(XUploadContentType,
361364 "X-Upload-Content-Type" ,
362365 Mime ) ;
363366
367+ #[ derive( Clone , PartialEq , Debug ) ]
368+ pub enum ByteRange {
369+ Any ,
370+ Chunk ( u64 , u64 )
371+ }
372+
373+ impl fmt:: Display for ByteRange {
374+ fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
375+ match * self {
376+ ByteRange :: Any => fmt. write_str ( "*" ) . ok ( ) ,
377+ ByteRange :: Chunk ( first, last) => write ! ( fmt, "{}-{}" , first, last) . ok ( )
378+ } ;
379+ Ok ( ( ) )
380+ }
381+ }
382+
383+ impl FromStr for ByteRange {
384+ type Err = & ' static str ;
385+
386+ /// NOTE: only implements `%i-%i`, not `*`
387+ fn from_str ( s : & str ) -> std:: result:: Result < ByteRange , & ' static str > {
388+ let parts: Vec < & str > = s. split ( '-' ) . collect ( ) ;
389+ if parts. len ( ) != 2 {
390+ return Err ( "Expected two parts: %i-%i" )
391+ }
392+ Ok (
393+ ByteRange :: Chunk (
394+ match FromStr :: from_str ( parts[ 0 ] ) {
395+ Ok ( d) => d,
396+ _ => return Err ( "Couldn't parse 'first' as digit" )
397+ } ,
398+ match FromStr :: from_str ( parts[ 1 ] ) {
399+ Ok ( d) => d,
400+ _ => return Err ( "Couldn't parse 'last' as digit" )
401+ }
402+ )
403+ )
404+ }
405+ }
406+
407+ /// Implements the Content-Range header, for serialization only
408+ #[ derive( Clone , PartialEq , Debug ) ]
409+ pub struct ContentRange {
410+ pub range : ByteRange ,
411+ pub total_length : u64 ,
412+ }
413+
414+ impl Header for ContentRange {
415+ fn header_name ( ) -> & ' static str {
416+ "Content-Range"
417+ }
418+
419+ /// We are not parsable, as parsing is done by the `Range` header
420+ fn parse_header ( raw : & [ Vec < u8 > ] ) -> Option < ContentRange > {
421+ None
422+ }
423+ }
424+
425+
426+ impl HeaderFormat for ContentRange {
427+ fn fmt_header ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
428+ write ! ( fmt, "bytes {}/{}" , self . range, self . total_length) . ok ( ) ;
429+ Ok ( ( ) )
430+ }
431+ }
432+
433+ #[ derive( Clone , PartialEq , Debug ) ]
434+ pub struct RangeResponseHeader ( pub ByteRange ) ;
435+
436+ impl Header for RangeResponseHeader {
437+ fn header_name ( ) -> & ' static str {
438+ "Range"
439+ }
440+
441+ fn parse_header ( raw : & [ Vec < u8 > ] ) -> Option < RangeResponseHeader > {
442+ match raw {
443+ [ ref v] => {
444+ if let Ok ( s) = std:: str:: from_utf8 ( v) {
445+ if s. starts_with ( "bytes=" ) {
446+ return Some ( RangeResponseHeader (
447+ match FromStr :: from_str ( & s[ 6 ..] ) {
448+ Ok ( br) => br,
449+ _ => return None
450+ }
451+ ) )
452+ }
453+ }
454+ None
455+ } ,
456+ _ => None
457+ }
458+ }
459+ }
460+
461+ impl HeaderFormat for RangeResponseHeader {
462+ /// No implmentation necessary, we just need to parse
463+ fn fmt_header ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
464+ Err ( fmt:: Error )
465+ }
466+ }
467+
364468/// A utility type to perform a resumable upload from start to end.
365469pub struct ResumableUploadHelper < ' a , NC : ' a , A : ' a > {
366470 pub client : & ' a mut hyper:: client:: Client < NC > ,
@@ -371,7 +475,7 @@ pub struct ResumableUploadHelper<'a, NC: 'a, A: 'a> {
371475 pub url : & ' a str ,
372476 pub reader : & ' a mut ReadSeek ,
373477 pub media_type : Mime ,
374- pub content_size : u64
478+ pub content_length : u64
375479}
376480
377481impl < ' a , NC , A > ResumableUploadHelper < ' a , NC , A >
@@ -381,6 +485,7 @@ impl<'a, NC, A> ResumableUploadHelper<'a, NC, A>
381485 fn query_transfer_status ( & ' a mut self ) -> ( u64 , hyper:: HttpResult < hyper:: client:: Response > ) {
382486 self . client . post ( self . url )
383487 . header ( UserAgent ( self . user_agent . to_string ( ) ) )
488+ . header ( ContentRange { range : ByteRange :: Any , total_length : self . content_length } )
384489 . header ( self . auth_header . clone ( ) ) ;
385490 ( 0 , Err ( hyper:: error:: HttpError :: HttpStatusError ) )
386491 }
0 commit comments