Skip to content

Commit

Permalink
fix(core): resolve HTTP API on non-ok status code, fix binary response,
Browse files Browse the repository at this point in the history
closes #2046 (#2053)
  • Loading branch information
lucasfernog committed Jun 23, 2021
1 parent c22e5a7 commit 47f7558
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changes/fix-http-binary-response.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Fixes the HTTP API binary response serialization.
6 changes: 6 additions & 0 deletions .changes/fix-http-resolve-error.md
@@ -0,0 +1,6 @@
---
"tauri": patch
"api": patch
---

The `http` APIs now resolve the returned promise when the API call finishes with an error status code.
5 changes: 0 additions & 5 deletions .changes/http-error-message.md

This file was deleted.

2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.js

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions core/tauri/src/api/error.rs
Expand Up @@ -32,9 +32,6 @@ pub enum Error {
#[cfg(feature = "reqwest-client")]
#[error("Network Error: {0}")]
Network(#[from] reqwest::Error),
/// HTTP request error. First parameter is the response status code, and the second is the response text.
#[error("HTTP Error: status code {0} and response `{1}`")]
Http(u16, String),
/// HTTP method error.
#[error("{0}")]
HttpMethod(#[from] http::method::InvalidMethod),
Expand Down
36 changes: 11 additions & 25 deletions core/tauri/src/api/http.rs
Expand Up @@ -118,18 +118,11 @@ impl Client {
request_builder.send()?
};

if response.is_success() {
Ok(Response(
request.response_type.unwrap_or(ResponseType::Json),
response,
request.url,
))
} else {
Err(super::Error::Http(
response.status().as_u16(),
response.text()?,
))
}
Ok(Response(
request.response_type.unwrap_or(ResponseType::Json),
response,
request.url,
))
}
}

Expand Down Expand Up @@ -183,17 +176,10 @@ impl Client {

let response = self.0.execute(http_request).await?;

if response.status().is_success() {
Ok(Response(
request.response_type.unwrap_or(ResponseType::Json),
response,
))
} else {
Err(super::Error::Http(
response.status().as_u16(),
response.text().await?,
))
}
Ok(Response(
request.response_type.unwrap_or(ResponseType::Json),
response,
))
}
}

Expand Down Expand Up @@ -368,14 +354,14 @@ impl Response {
let data = match self.0 {
ResponseType::Json => self.1.json().await?,
ResponseType::Text => Value::String(self.1.text().await?),
ResponseType::Binary => Value::String(serde_json::to_string(&self.1.bytes().await?)?),
ResponseType::Binary => serde_json::to_value(&self.1.bytes().await?)?,
};

#[cfg(not(feature = "reqwest-client"))]
let data = match self.0 {
ResponseType::Json => self.1.json()?,
ResponseType::Text => Value::String(self.1.text()?),
ResponseType::Binary => Value::String(serde_json::to_string(&self.1.bytes()?)?),
ResponseType::Binary => serde_json::to_value(&self.1.bytes()?)?,
};

Ok(ResponseData {
Expand Down
47 changes: 45 additions & 2 deletions tooling/api/src/http.ts
Expand Up @@ -123,16 +123,35 @@ type RequestOptions = Omit<HttpOptions, 'method' | 'url'>
/** Options for the `fetch` API. */
type FetchOptions = Omit<HttpOptions, 'url'>

/** @ignore */
interface IResponse<T> {
url: string
status: number
headers: Record<string, string>
data: T
}

/** Response object. */
interface Response<T> {
class Response<T> {
/** The request URL. */
url: string
/** The response status code. */
status: number
/** A boolean indicating whether the response was successful (status in the range 200–299) or not. */
ok: boolean
/** The response headers. */
headers: Record<string, string>
/** The response data. */
data: T

/** @ignore */
constructor(response: IResponse<T>) {
this.url = response.url
this.status = response.status
this.ok = this.status >= 200 && this.status < 300
this.headers = response.headers
this.data = response.data
}
}

class Client {
Expand Down Expand Up @@ -164,13 +183,37 @@ class Client {
* @returns A promise resolving to the response.
*/
async request<T>(options: HttpOptions): Promise<Response<T>> {
return invokeTauriCommand({
const jsonResponse =
!options.responseType || options.responseType === ResponseType.JSON
if (jsonResponse) {
options.responseType = ResponseType.Text
}
return invokeTauriCommand<IResponse<T>>({
__tauriModule: 'Http',
message: {
cmd: 'httpRequest',
client: this.id,
options
}
}).then((res) => {
const response = new Response(res)
if (jsonResponse) {
/* eslint-disable */
try {
// @ts-expect-error
response.data = JSON.parse(response.data as string)
} catch (e) {
if (response.ok) {
throw Error(
`Failed to parse response \`${response.data}\` as JSON: ${e};
try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`
)
}
}
/* eslint-enable */
return response
}
return response
})
}

Expand Down

0 comments on commit 47f7558

Please sign in to comment.