Compare commits

...

4 Commits

Author SHA1 Message Date
Mikayla
e045f266ec WIP 2024-07-29 12:56:38 -07:00
Mikayla
693be12929 Remove references to isahc 2024-07-28 15:17:34 -07:00
Mikayla
6cf0a701f9 Turn the request and response types into an http crate compatible Body type 2024-07-28 14:54:20 -07:00
Mikayla
65d88e3bfb Implement request and response types 2024-07-28 14:12:14 -07:00
27 changed files with 356 additions and 130 deletions

86
Cargo.lock generated
View File

@@ -274,7 +274,6 @@ dependencies = [
"anyhow",
"futures 0.3.28",
"http_client",
"isahc",
"schemars",
"serde",
"serde_json",
@@ -960,7 +959,6 @@ dependencies = [
"editor",
"gpui",
"http_client",
"isahc",
"log",
"markdown_preview",
"menu",
@@ -1300,7 +1298,7 @@ dependencies = [
"once_cell",
"pin-project-lite",
"pin-utils",
"rustls",
"rustls 0.21.12",
"tokio",
"tracing",
]
@@ -3897,7 +3895,6 @@ dependencies = [
"gpui",
"http_client",
"indexed_docs",
"isahc",
"language",
"log",
"lsp",
@@ -4056,7 +4053,6 @@ dependencies = [
"gpui",
"http_client",
"human_bytes",
"isahc",
"language",
"log",
"menu",
@@ -4706,7 +4702,6 @@ dependencies = [
"git",
"gpui",
"http_client",
"isahc",
"pretty_assertions",
"regex",
"serde",
@@ -5266,6 +5261,8 @@ dependencies = [
"log",
"serde",
"serde_json",
"smol",
"ureq",
"url",
]
@@ -5327,7 +5324,7 @@ dependencies = [
"http 0.2.9",
"hyper",
"log",
"rustls",
"rustls 0.21.12",
"rustls-native-certs",
"tokio",
"tokio-rustls",
@@ -7134,7 +7131,6 @@ dependencies = [
"anyhow",
"futures 0.3.28",
"http_client",
"isahc",
"schemars",
"serde",
"serde_json",
@@ -7203,7 +7199,6 @@ dependencies = [
"anyhow",
"futures 0.3.28",
"http_client",
"isahc",
"schemars",
"serde",
"serde_json",
@@ -9115,10 +9110,25 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [
"log",
"ring",
"rustls-webpki",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.23.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.6",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-native-certs"
version = "0.6.3"
@@ -9140,6 +9150,12 @@ dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustls-pki-types"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -9150,6 +9166,17 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@@ -10084,7 +10111,7 @@ dependencies = [
"paste",
"percent-encoding",
"rust_decimal",
"rustls",
"rustls 0.21.12",
"rustls-pemfile",
"serde",
"serde_json",
@@ -10098,7 +10125,7 @@ dependencies = [
"tracing",
"url",
"uuid",
"webpki-roots",
"webpki-roots 0.24.0",
]
[[package]]
@@ -11184,7 +11211,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls",
"rustls 0.21.12",
"tokio",
]
@@ -11846,6 +11873,23 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
dependencies = [
"base64 0.22.0",
"flate2",
"http 0.2.9",
"log",
"once_cell",
"rustls 0.23.12",
"rustls-pki-types",
"url",
"webpki-roots 0.26.3",
]
[[package]]
name = "url"
version = "2.5.2"
@@ -12666,7 +12710,16 @@ version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888"
dependencies = [
"rustls-webpki",
"rustls-webpki 0.101.7",
]
[[package]]
name = "webpki-roots"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
dependencies = [
"rustls-pki-types",
]
[[package]]
@@ -13669,7 +13722,6 @@ dependencies = [
"image_viewer",
"inline_completion_button",
"install_cli",
"isahc",
"journal",
"language",
"language_model",
@@ -13985,9 +14037,9 @@ dependencies = [
[[package]]
name = "zeroize"
version = "1.6.0"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]

View File

@@ -347,10 +347,6 @@ ignore = "0.4.22"
image = "0.25.1"
indexmap = { version = "1.6.2", features = ["serde"] }
indoc = "2"
# We explicitly disable http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = [
"text-decoding",
] }
itertools = "0.11.0"
jsonwebtoken = "9.3"
lazy_static = "1.4.0"

View File

@@ -19,7 +19,6 @@ path = "src/anthropic.rs"
anyhow.workspace = true
futures.workspace = true
http_client.workspace = true
isahc.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true

View File

@@ -1,7 +1,6 @@
use anyhow::{anyhow, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use isahc::config::Configurable;
use http_client::{HttpBody, HttpClient, Method, Request as HttpRequest};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, time::Duration};
use strum::EnumIter;
@@ -184,10 +183,11 @@ pub async fn stream_completion(
.header("Anthropic-Beta", "tools-2024-04-04")
.header("X-Api-Key", api_key)
.header("Content-Type", "application/json");
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
}
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
// TODO: figure out what to do about low_speed_timeout
// if let Some(low_speed_timeout) = low_speed_timeout {
// request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
// }
let request = request_builder.body(HttpBody::from(serde_json::to_string(&request)?))?;
let mut response = client.send(request).await?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());

View File

@@ -10,7 +10,7 @@ use assistant_slash_command::{
use futures::AsyncReadExt;
use gpui::{AppContext, Task, WeakView};
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use http_client::{HttpBody, HttpClient, HttpClientWithUrl};
use language::LspAdapterDelegate;
use ui::prelude::*;
use workspace::Workspace;
@@ -31,7 +31,7 @@ impl FetchSlashCommand {
url = format!("https://{url}");
}
let mut response = http_client.get(&url, AsyncBody::default(), true).await?;
let mut response = http_client.get(&url, HttpBody::default(), true).await?;
let mut body = Vec::new();
response

View File

@@ -19,7 +19,6 @@ db.workspace = true
editor.workspace = true
gpui.workspace = true
http_client.workspace = true
isahc.workspace = true
log.workspace = true
markdown_preview.workspace = true
menu.workspace = true

View File

@@ -9,7 +9,7 @@ use gpui::{
actions, AppContext, AsyncAppContext, Context as _, Global, Model, ModelContext,
SemanticVersion, SharedString, Task, View, ViewContext, VisualContext, WindowContext,
};
use isahc::AsyncBody;
use http_client::HttpBody;
use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
use schemars::JsonSchema;
@@ -583,7 +583,7 @@ async fn download_remote_server_binary(
(installation_id, release_channel, telemetry)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
let request_body = HttpBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,
@@ -611,7 +611,7 @@ async fn download_release(
(installation_id, release_channel, telemetry)
})?;
let request_body = AsyncBody::from(serde_json::to_string(&UpdateRequestBody {
let request_body = HttpBody::from(serde_json::to_string(&UpdateRequestBody {
installation_id,
release_channel,
telemetry,

View File

@@ -21,7 +21,7 @@ use futures::{
use gpui::{
actions, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Global, Model, Task, WeakModel,
};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use http_client::{HttpBody, HttpClient, HttpClientWithUrl};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use postage::watch;
@@ -1383,7 +1383,7 @@ impl Client {
let mut url = self.rpc_url(http.clone(), None).await?;
url.set_path("/user");
url.set_query(Some(&format!("github_login={login}")));
let request: http_client::Request<AsyncBody> = Request::get(url.as_str())
let request: http_client::Request<HttpBody> = Request::get(url.as_str())
.header("Authorization", format!("token {api_token}"))
.body("".into())?;

View File

@@ -28,7 +28,6 @@ futures.workspace = true
gpui.workspace = true
http_client.workspace = true
indexed_docs.workspace = true
isahc.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true

View File

@@ -6,7 +6,7 @@ use async_compression::futures::bufread::GzipDecoder;
use async_tar::Archive;
use futures::io::BufReader;
use futures::AsyncReadExt;
use http_client::{self, AsyncBody, HttpClient};
use http_client::{self, HttpBody, HttpClient};
use serde::Deserialize;
use std::{
env, fs, mem,
@@ -360,7 +360,7 @@ impl ExtensionBuilder {
);
let mut response = self
.http
.get(WASI_ADAPTER_URL, AsyncBody::default(), true)
.get(WASI_ADAPTER_URL, HttpBody::default(), true)
.await?;
let mut content = Vec::new();
@@ -398,7 +398,7 @@ impl ExtensionBuilder {
fs::remove_dir_all(&tar_out_dir).ok();
log::info!("downloading wasi-sdk to {}", wasi_sdk_dir.display());
let mut response = self.http.get(&url, AsyncBody::default(), true).await?;
let mut response = self.http.get(&url, HttpBody::default(), true).await?;
let body = BufReader::new(response.body_mut());
let body = GzipDecoder::new(body);
let tar = Archive::new(body);

View File

@@ -33,7 +33,7 @@ use gpui::{
actions, AppContext, AsyncAppContext, Context, EventEmitter, Global, Model, ModelContext, Task,
WeakModel,
};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use http_client::{HttpBody, HttpClient, HttpClientWithUrl};
use indexed_docs::{IndexedDocsRegistry, ProviderId};
use language::{
LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
@@ -584,7 +584,7 @@ impl ExtensionStore {
let http_client = self.http_client.clone();
cx.spawn(move |_, _| async move {
let mut response = http_client
.get(&url?.as_ref(), AsyncBody::empty(), true)
.get(&url?.as_ref(), HttpBody::empty(), true)
.await?;
let mut body = Vec::new();
@@ -664,7 +664,7 @@ impl ExtensionStore {
let content_length = response
.headers()
.get(isahc::http::header::CONTENT_LENGTH)
.get(http_client::http::header::CONTENT_LENGTH)
.and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
let mut body = BufReader::new(response.body_mut());

View File

@@ -1,5 +1,5 @@
use crate::wasm_host::{wit::ToWasmtimeResult, WasmState};
use ::http_client::AsyncBody;
use ::http_client::HttpBody;
use ::settings::Settings;
use anyhow::{anyhow, bail, Context, Result};
use async_compression::futures::bufread::GzipDecoder;
@@ -138,7 +138,7 @@ impl http_client::Host for WasmState {
let mut response = self
.host
.http_client
.get(url, AsyncBody::default(), true)
.get(url, HttpBody::default(), true)
.await?;
if response.status().is_client_error() || response.status().is_server_error() {

View File

@@ -23,7 +23,6 @@ editor.workspace = true
futures.workspace = true
gpui.workspace = true
human_bytes = "0.4.1"
isahc.workspace = true
http_client.workspace = true
language.workspace = true
log.workspace = true

View File

@@ -11,7 +11,7 @@ use gpui::{
PromptLevel, Render, Task, View, ViewContext,
};
use http_client::HttpClient;
use isahc::Request;
use http_client::Request;
use language::Buffer;
use project::Project;
use regex::Regex;

View File

@@ -18,7 +18,6 @@ futures.workspace = true
git.workspace = true
gpui.workspace = true
http_client.workspace = true
isahc.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true

View File

@@ -4,8 +4,7 @@ use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use futures::AsyncReadExt;
use http_client::HttpClient;
use isahc::config::Configurable;
use isahc::{AsyncBody, Request};
use http_client::{HttpBody, Request};
use serde::Deserialize;
use url::Url;
@@ -52,7 +51,8 @@ impl Codeberg {
format!("https://codeberg.org/api/v1/repos/{repo_owner}/{repo}/git/commits/{commit}");
let mut request = Request::get(&url)
.redirect_policy(isahc::config::RedirectPolicy::Follow)
// TODO: replicate this API?
// .redirect_policy(isahc::config::RedirectPolicy::Follow)
.header("Content-Type", "application/json");
if let Ok(codeberg_token) = std::env::var("CODEBERG_TOKEN") {
@@ -60,7 +60,7 @@ impl Codeberg {
}
let mut response = client
.send(request.body(AsyncBody::default())?)
.send(request.body(HttpBody::default())?)
.await
.with_context(|| format!("error fetching Codeberg commit details at {:?}", url))?;

View File

@@ -4,8 +4,7 @@ use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use futures::AsyncReadExt;
use http_client::HttpClient;
use isahc::config::Configurable;
use isahc::{AsyncBody, Request};
use http_client::{HttpBody, Request};
use regex::Regex;
use serde::Deserialize;
use url::Url;
@@ -55,16 +54,14 @@ impl Github {
) -> Result<Option<User>> {
let url = format!("https://api.github.com/repos/{repo_owner}/{repo}/commits/{commit}");
let mut request = Request::get(&url)
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.header("Content-Type", "application/json");
let mut request = Request::get(&url).header("Content-Type", "application/json");
if let Ok(github_token) = std::env::var("GITHUB_TOKEN") {
request = request.header("Authorization", format!("Bearer {}", github_token));
}
let mut response = client
.send(request.body(AsyncBody::default())?)
.send_with_redirect_policy(request.body(HttpBody::default())?, true)
.await
.with_context(|| format!("error fetching GitHub commit details at {:?}", url))?;

View File

@@ -20,9 +20,14 @@ http = "1.0.0"
anyhow.workspace = true
derive_more.workspace = true
futures.workspace = true
isahc.workspace = true
# We explicitly disable http2 support in isahc.
isahc = { version = "1.7.2", default-features = false, features = [
"text-decoding",
] }
log.workspace = true
serde.workspace = true
serde_json.workspace = true
futures-lite.workspace = true
url.workspace = true
smol.workspace = true
ureq = { version = "2.10.0", features = ["http-interop"] }

View File

@@ -2,33 +2,177 @@ pub mod github;
pub use anyhow::{anyhow, Result};
use derive_more::Deref;
use futures::future::BoxFuture;
use futures::{future::BoxFuture, AsyncRead, AsyncReadExt as _};
use futures_lite::FutureExt;
use isahc::config::{Configurable, RedirectPolicy};
pub use isahc::http;
use isahc::AsyncBody as IsahcBody;
pub use isahc::{
http::{Method, StatusCode, Uri},
AsyncBody, Error, HttpClient as IsahcHttpClient, Request, Response,
http::{Method, Request, Response, StatusCode, Uri},
Error, HttpClient as IsahcHttpClient,
};
use smol::{unblock, Unblock};
#[cfg(feature = "test-support")]
use std::fmt;
use std::{
borrow::Cow,
io::{Cursor, Read},
pin::Pin,
sync::{Arc, Mutex},
task::Poll,
time::Duration,
};
pub use url::Url;
/// The body of an HTTP Request
pub type HttpBody = IsahcBody;
// A type to implement Read on the inner dyn Read
struct UreqReader(Box<dyn Read + Send + 'static>);
impl Read for UreqReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0.read(buf)
}
}
/// Based on the implementation of AsyncBody in
/// https://github.com/sagebind/isahc/blob/5c533f1ef4d6bdf1fd291b5103c22110f41d0bf0/src/body/mod.rs
struct HttpBodyNew(Inner);
enum Inner {
/// An empty body.
Empty,
/// A body stored in memory.
SyncReader(std::io::Cursor<Cow<'static, [u8]>>),
/// An asynchronous reader.
AsyncReader(Pin<Box<dyn futures::AsyncRead + Send + Sync>>),
/// A compatibility layer over our old isahc client, to make it compatible with ureq
UReqReader(smol::Unblock<UreqReader>),
}
impl HttpBodyNew {
/// Create a streaming body that reads from the given reader.
pub fn from_reader<R>(read: R) -> Self
where
R: AsyncRead + Send + Sync + 'static,
{
Self(Inner::AsyncReader(Box::pin(read)))
}
}
impl std::io::Read for HttpBodyNew {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match &mut self.0 {
Inner::Empty => Ok(0),
Inner::SyncReader(cursor) => cursor.read(buf),
Inner::AsyncReader(async_reader) => smol::block_on(async_reader.read(buf)),
Inner::UReqReader(unblock_reader) => smol::block_on(unblock_reader.read(buf)),
}
}
}
impl futures::AsyncRead for HttpBodyNew {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut [u8],
) -> std::task::Poll<std::io::Result<usize>> {
// SAFETY: Standard Enum pin projection
let inner = unsafe { &mut self.get_unchecked_mut().0 };
match inner {
Inner::Empty => Poll::Ready(Ok(0)),
// Blocking call is over an in-memory buffer
Inner::SyncReader(cursor) => Poll::Ready(cursor.read(buf)),
Inner::AsyncReader(async_reader) => {
AsyncRead::poll_read(async_reader.as_mut(), cx, buf)
}
Inner::UReqReader(unblock_reader) => AsyncRead::poll_read(
unsafe { Pin::new_unchecked(unblock_reader).as_mut() },
cx,
buf,
),
}
}
}
impl Default for HttpBodyNew {
fn default() -> Self {
Self(Inner::Empty)
}
}
impl From<()> for HttpBodyNew {
fn from(_: ()) -> Self {
Self(Inner::Empty)
}
}
impl From<Vec<u8>> for HttpBodyNew {
fn from(body: Vec<u8>) -> Self {
Self(Inner::SyncReader(Cursor::new(Cow::Owned(body))))
}
}
impl From<&'_ [u8]> for HttpBodyNew {
fn from(body: &[u8]) -> Self {
body.to_vec().into()
}
}
impl From<String> for HttpBodyNew {
fn from(body: String) -> Self {
body.into_bytes().into()
}
}
impl From<&'_ str> for HttpBodyNew {
fn from(body: &str) -> Self {
body.as_bytes().into()
}
}
impl<T: Into<Self>> From<Option<T>> for HttpBodyNew {
fn from(body: Option<T>) -> Self {
match body {
Some(body) => body.into(),
None => Self(Inner::Empty),
}
}
}
impl From<ureq::Response> for HttpBodyNew {
fn from(value: ureq::Response) -> Self {
HttpBodyNew(Inner::UReqReader(Unblock::new(UreqReader(
value.into_reader(),
))))
}
}
pub trait HttpClient: Send + Sync {
// TODO: Make a better API for this once we have ureq in place
fn send_with_redirect_policy(
&self,
req: Request<HttpBody>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>>;
fn send(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>>;
req: Request<HttpBody>,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
self.send_with_redirect_policy(req, false)
}
fn get<'a>(
&'a self,
uri: &str,
body: AsyncBody,
body: HttpBody,
follow_redirects: bool,
) -> BoxFuture<'a, Result<Response<AsyncBody>, Error>> {
) -> BoxFuture<'a, Result<Response<HttpBody>, Error>> {
let request = isahc::Request::builder()
.redirect_policy(if follow_redirects {
RedirectPolicy::Follow
@@ -47,8 +191,8 @@ pub trait HttpClient: Send + Sync {
fn post_json<'a>(
&'a self,
uri: &str,
body: AsyncBody,
) -> BoxFuture<'a, Result<Response<AsyncBody>, Error>> {
body: HttpBody,
) -> BoxFuture<'a, Result<Response<HttpBody>, Error>> {
let request = isahc::Request::builder()
.method(Method::POST)
.uri(uri)
@@ -91,11 +235,12 @@ impl HttpClientWithProxy {
}
impl HttpClient for HttpClientWithProxy {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
self.client.send(req)
req: Request<HttpBody>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
self.client.send_with_redirect_policy(req, follow_redirects)
}
fn proxy(&self) -> Option<&Uri> {
@@ -104,11 +249,12 @@ impl HttpClient for HttpClientWithProxy {
}
impl HttpClient for Arc<HttpClientWithProxy> {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
self.client.send(req)
req: Request<HttpBody>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
self.client.send_with_redirect_policy(req, follow_redirects)
}
fn proxy(&self) -> Option<&Uri> {
@@ -174,11 +320,12 @@ impl HttpClientWithUrl {
}
impl HttpClient for Arc<HttpClientWithUrl> {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
self.client.send(req)
req: Request<HttpBody>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
self.client.send_with_redirect_policy(req, follow_redirects)
}
fn proxy(&self) -> Option<&Uri> {
@@ -187,11 +334,12 @@ impl HttpClient for Arc<HttpClientWithUrl> {
}
impl HttpClient for HttpClientWithUrl {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
self.client.send(req)
req: Request<HttpBody>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
self.client.send_with_redirect_policy(req, follow_redirects)
}
fn proxy(&self) -> Option<&Uri> {
@@ -233,10 +381,11 @@ fn read_proxy_from_env() -> Option<Uri> {
}
impl HttpClient for isahc::HttpClient {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
req: Request<HttpBody>,
_follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
let client = self.clone();
Box::pin(async move { client.send_async(req).await })
}
@@ -246,9 +395,43 @@ impl HttpClient for isahc::HttpClient {
}
}
struct UreqHttpClient {
redirecting_agent: ureq::Agent,
no_follow_agent: ureq::Agent,
}
impl UreqHttpClient {
fn send_with_redirect_policy(
&self,
req: Request<HttpBodyNew>,
follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, anyhow::Error>> {
let agent = if follow_redirects {
self.redirecting_agent
} else {
self.no_follow_agent
}
.clone();
Box::pin(async move {
unblock(move || {
let (parts, body) = req.into_parts();
let request: ureq::Request = parts.into();
// agent.send_request(path)
Err(anyhow!("TODO"))
})
})
}
fn proxy(&self) -> Option<&Uri> {
None
}
}
#[cfg(feature = "test-support")]
type FakeHttpHandler = Box<
dyn Fn(Request<AsyncBody>) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>>
dyn Fn(Request<HttpBody>) -> BoxFuture<'static, Result<Response<HttpBody>, Error>>
+ Send
+ Sync
+ 'static,
@@ -263,8 +446,8 @@ pub struct FakeHttpClient {
impl FakeHttpClient {
pub fn create<Fut, F>(handler: F) -> Arc<HttpClientWithUrl>
where
Fut: futures::Future<Output = Result<Response<AsyncBody>, Error>> + Send + 'static,
F: Fn(Request<AsyncBody>) -> Fut + Send + Sync + 'static,
Fut: futures::Future<Output = Result<Response<HttpBody>, Error>> + Send + 'static,
F: Fn(Request<HttpBody>) -> Fut + Send + Sync + 'static,
{
Arc::new(HttpClientWithUrl {
base_url: Mutex::new("http://test.example".into()),
@@ -305,10 +488,11 @@ impl fmt::Debug for FakeHttpClient {
#[cfg(feature = "test-support")]
impl HttpClient for FakeHttpClient {
fn send(
fn send_with_redirect_policy(
&self,
req: Request<AsyncBody>,
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
req: Request<HttpBody>,
_follow_redirects: bool,
) -> BoxFuture<'static, Result<Response<HttpBody>, Error>> {
let future = (self.handler)(req);
Box::pin(async move { future.await.map(Into::into) })
}

View File

@@ -13,7 +13,7 @@ use async_trait::async_trait;
use collections::{HashSet, VecDeque};
use fs::Fs;
use futures::{AsyncReadExt, FutureExt};
use http_client::{AsyncBody, HttpClient, HttpClientWithUrl};
use http_client::{HttpBody, HttpClient, HttpClientWithUrl};
use crate::{IndexedDocsDatabase, IndexedDocsProvider, PackageName, ProviderId};
@@ -135,7 +135,7 @@ impl IndexedDocsProvider for DocsDotRsProvider {
let mut response = http_client
.get(
&format!("https://docs.rs/{path}"),
AsyncBody::default(),
HttpBody::default(),
true,
)
.await?;

View File

@@ -19,7 +19,6 @@ schemars = ["dep:schemars"]
anyhow.workspace = true
futures.workspace = true
http_client.workspace = true
isahc.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true

View File

@@ -1,7 +1,6 @@
use anyhow::{anyhow, Context, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, StreamExt};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use isahc::config::Configurable;
use http_client::{HttpBody, HttpClient, Method, Request as HttpRequest};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, sync::Arc, time::Duration};
@@ -174,11 +173,12 @@ pub async fn stream_chat_completion(
.uri(uri)
.header("Content-Type", "application/json");
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
};
// TODO: Figure out what to do about this
// if let Some(low_speed_timeout) = low_speed_timeout {
// request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
// };
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
let request = request_builder.body(HttpBody::from(serde_json::to_string(&request)?))?;
let mut response = client.send(request).await?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());
@@ -217,11 +217,12 @@ pub async fn get_models(
.uri(uri)
.header("Accept", "application/json");
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
};
// TODO: Figure out what to do about this
// if let Some(low_speed_timeout) = low_speed_timeout {
// request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
// };
let request = request_builder.body(AsyncBody::default())?;
let request = request_builder.body(HttpBody::default())?;
let mut response = client.send(request).await?;
@@ -249,12 +250,10 @@ pub async fn preload_model(client: Arc<dyn HttpClient>, api_url: &str, model: &s
.method(Method::POST)
.uri(uri)
.header("Content-Type", "application/json")
.body(AsyncBody::from(serde_json::to_string(
&serde_json::json!({
"model": model,
"keep_alive": "15m",
}),
)?))?;
.body(HttpBody::from(serde_json::to_string(&serde_json::json!({
"model": model,
"keep_alive": "15m",
}))?))?;
let mut response = match client.send(request).await {
Ok(response) => response,

View File

@@ -19,7 +19,6 @@ schemars = ["dep:schemars"]
anyhow.workspace = true
futures.workspace = true
http_client.workspace = true
isahc.workspace = true
schemars = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true

View File

@@ -1,7 +1,6 @@
use anyhow::{anyhow, Context, Result};
use futures::{io::BufReader, stream::BoxStream, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
use http_client::{AsyncBody, HttpClient, Method, Request as HttpRequest};
use isahc::config::Configurable;
use http_client::{HttpBody, HttpClient, Method, Request as HttpRequest};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::{convert::TryFrom, future::Future, time::Duration};
@@ -238,11 +237,12 @@ pub async fn stream_completion(
.header("Content-Type", "application/json")
.header("Authorization", format!("Bearer {}", api_key));
if let Some(low_speed_timeout) = low_speed_timeout {
request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
};
// TODO: Figure out what to do about this
// if let Some(low_speed_timeout) = low_speed_timeout {
// request_builder = request_builder.low_speed_timeout(100, low_speed_timeout);
// };
let request = request_builder.body(AsyncBody::from(serde_json::to_string(&request)?))?;
let request = request_builder.body(HttpBody::from(serde_json::to_string(&request)?))?;
let mut response = client.send(request).await?;
if response.status().is_success() {
let reader = BufReader::new(response.into_body());
@@ -331,7 +331,7 @@ pub fn embed<'a>(
model,
input: texts.into_iter().collect(),
};
let body = AsyncBody::from(serde_json::to_string(&request).unwrap());
let body = HttpBody::from(serde_json::to_string(&request).unwrap());
let request = HttpRequest::builder()
.method(Method::POST)
.uri(uri)

View File

@@ -1,7 +1,7 @@
use anyhow::{anyhow, Context, Result};
use futures::io::BufReader;
use futures::{AsyncReadExt, Future};
use http_client::{AsyncBody, HttpClient, Request as HttpRequest};
use http_client::{HttpBody, HttpClient, Request as HttpRequest};
use paths::supermaven_dir;
use serde::{Deserialize, Serialize};
use smol::fs::{self, File};
@@ -79,7 +79,7 @@ impl SupermavenAdminApi {
let mut response = self
.http_client
.send(request.body(AsyncBody::default())?)
.send(request.body(HttpBody::default())?)
.await
.with_context(|| "Unable to get Supermaven API Key".to_string())?;
@@ -114,7 +114,7 @@ impl SupermavenAdminApi {
let request = HttpRequest::post(&uri)
.header("Authorization", self.admin_api_key.clone())
.body(AsyncBody::from(serde_json::to_vec(&request)?))?;
.body(HttpBody::from(serde_json::to_vec(&request)?))?;
let mut response = self
.http_client
@@ -143,7 +143,7 @@ impl SupermavenAdminApi {
let mut response = self
.http_client
.send(request.body(AsyncBody::default())?)
.send(request.body(HttpBody::default())?)
.await
.with_context(|| "Unable to delete Supermaven User".to_string())?;
@@ -194,7 +194,7 @@ pub async fn latest_release(
let request = HttpRequest::get(&uri);
let mut response = client
.send(request.body(AsyncBody::default())?)
.send(request.body(HttpBody::default())?)
.await
.with_context(|| "Unable to acquire Supermaven Agent".to_string())?;
@@ -258,7 +258,7 @@ pub fn get_supermaven_agent_path(
let request = HttpRequest::get(&download_info.download_url);
let mut response = client
.send(request.body(AsyncBody::default())?)
.send(request.body(HttpBody::default())?)
.await
.with_context(|| "Unable to download Supermaven Agent".to_string())?;

View File

@@ -53,7 +53,6 @@ http_client.workspace = true
image_viewer.workspace = true
inline_completion_button.workspace = true
install_cli.workspace = true
isahc.workspace = true
journal.workspace = true
language.workspace = true
language_model.workspace = true

View File

@@ -5,7 +5,6 @@ use client::telemetry;
use db::kvp::KEY_VALUE_STORE;
use gpui::{AppContext, SemanticVersion};
use http_client::Method;
use isahc::config::Configurable;
use http_client::{self, HttpClient, HttpClientWithUrl};
use paths::{crashes_dir, crashes_retired_dir};
@@ -489,7 +488,6 @@ async fn upload_previous_crashes(
.context("error reading crash file")?;
let mut request = http_client::Request::post(&crash_report_url.to_string())
.redirect_policy(isahc::config::RedirectPolicy::Follow)
.header("Content-Type", "text/plain");
if let Some((panicked_on, payload)) = most_recent_panic.as_ref() {
@@ -503,7 +501,10 @@ async fn upload_previous_crashes(
let request = request.body(body.into())?;
let response = http.send(request).await.context("error sending crash")?;
let response = http
.send_with_redirect_policy(request, true)
.await
.context("error sending crash")?;
if !response.status().is_success() {
log::error!("Error uploading crash to server: {}", response.status());
}