1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2019-2020 Dropbox, Inc.

//! Everything needed to implement your HTTP client.

use std::io::Read;

/// The base HTTP client trait.
pub trait HttpClient {
    /// Make a HTTP request.
    #[allow(clippy::too_many_arguments)]
    fn request(
        &self,
        endpoint: Endpoint,
        style: Style,
        function: &str,
        params: String,
        params_type: ParamsType,
        body: Option<&[u8]>,
        range_start: Option<u64>,
        range_end: Option<u64>,
    ) -> crate::Result<HttpRequestResultRaw>;
}

/// Marker trait to indicate that a HTTP client supports unauthenticated routes.
pub trait NoauthClient: HttpClient {}

/// Marker trait to indicate that a HTTP client supports User authentication.
/// Team authentication works by adding a `Authorization: Bearer <TOKEN>` header.
pub trait UserAuthClient: HttpClient {}

/// Marker trait to indicate that a HTTP client supports Team authentication.
/// Team authentication works by adding a `Authorization: Bearer <TOKEN>` header, and optionally a
/// `Dropbox-API-Select-Admin` or `Dropbox-API-Select-User` header.
pub trait TeamAuthClient: HttpClient {}

/// Marker trait to indicate that a HTTP client supports App authentication.
/// App authentication works by adding a `Authorization: Basic <base64(APP_KEY:APP_SECRET)>` header
/// to the HTTP request.
pub trait AppAuthClient: HttpClient {}

/// The raw response from the server, containing the result from either a header or the body, as
/// appropriate to the request style, and a body stream if it is from a Download style request.
pub struct HttpRequestResultRaw {
    /// The response body JSON string.
    pub result_json: String,

    /// The value of the `Content-Length` header in the response, if any. Only expected to not be
    /// `None` if `body` is also not `None`.
    pub content_length: Option<u64>,

    /// The response body stream, if any. Only expected to not be `None` for [`Style::Download`]
    /// endpoints.
    pub body: Option<Box<dyn Read>>,
}

/// The response from the server, parsed into a given type, including a body stream if it is from
/// a Download style request.
pub struct HttpRequestResult<T> {
    /// The API result, parsed into the given type.
    pub result: T,

    /// The value of the `Content-Length` header in the response, if any. Only expected to not be
    /// `None` if `body` is also not `None`.
    pub content_length: Option<u64>,

    /// The response body stream, if any. Only expected to not be `None` for [`Style::Download`]
    /// endpoints.
    pub body: Option<Box<dyn Read>>,
}

/// The API base endpoint for a request. Determines which hostname the request should go to.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Endpoint {
    /// The endpoint used for most API calls.
    Api,

    /// The endpoint primarily used for upload and download calls.
    Content,

    /// The endpoint primarily used for longpolling calls.
    Notify,

    /// The endpoint used for OAuth2 token requests.
    OAuth2,
}

impl Endpoint {
    /// The base URL for API calls using the given endpoint.
    pub fn url(self) -> &'static str {
        match self {
            Endpoint::Api => "https://api.dropboxapi.com/2/",
            Endpoint::Content => "https://content.dropboxapi.com/2/",
            Endpoint::Notify => "https://notify.dropboxapi.com/2/",
            Endpoint::OAuth2 => "https://api.dropboxapi.com/", // note no '2/'
        }
    }
}

/// The style of a request, which determines how arguments are passed, and whether there is a
/// request and/or response body.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Style {
    /// Arguments are passed in the request body; response is in the body; no request or response
    /// body content stream.
    Rpc,

    /// Arguments are passed in a HTTP header; response is in the body; request body is the upload
    /// content; no response body content stream.
    Upload,

    /// Arguments are passed in a HTTP header; response is in a HTTP header; no request content
    /// body; response body contains the content stream.
    Download,
}

/// The format of arguments being sent in a request.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ParamsType {
    /// JSON.
    Json,

    /// WWW Form URL-encoded. Only used for OAuth2 requests.
    Form,
}

impl ParamsType {
    /// The value for the HTTP Content-Type header for the given params format.
    pub fn content_type(self) -> &'static str {
        match self {
            ParamsType::Json => "application/json",
            ParamsType::Form => "application/x-www-form-urlencoded",
        }
    }
}

/// Used with Team Authentication to select a user context within that team.
#[derive(Debug, Clone)]
pub enum TeamSelect {
    /// A team member's user ID.
    User(String),

    /// A team admin's user ID, which grants additional access.
    Admin(String),
}

impl TeamSelect {
    /// The name of the HTTP header that must be set.
    pub fn header_name(&self) -> &'static str {
        match self {
            TeamSelect::User(_) => "Dropbox-API-Select-User",
            TeamSelect::Admin(_) => "Dropbox-API-Select-Admin",
        }
    }
}