1- use crate :: dbg_msg;
2- use crate :: oauth:: { force_refresh_token, token_daemon, Oauth , OauthBackendImpl } ;
3- use crate :: server:: RequestExt ;
4- use crate :: utils:: { format_url, Post } ;
51use arc_swap:: ArcSwap ;
62use cached:: proc_macro:: cached;
73use futures_lite:: future:: block_on;
84use futures_lite:: { future:: Boxed , FutureExt } ;
9- use hyper:: { body:: Buf , header, Body , Request as HyperRequest , Response as HyperResponse } ;
5+ use http_body_util:: BodyExt ;
6+ use hyper:: { header, Method , Request , Response } ;
107use log:: { error, info, trace, warn} ;
118use percent_encoding:: { percent_encode, CONTROLS } ;
129use serde_json:: Value ;
13- use std:: result:: Result ;
10+ use wreq:: redirect:: Policy ;
11+ use wreq:: { Client as WreqClient , EmulationFactory , header as wreq_header, Response as WreqResponse } ;
12+ use wreq_util:: { Emulation , EmulationOS , EmulationOption } ;
13+
1414use std:: sync:: atomic:: Ordering ;
1515use std:: sync:: atomic:: { AtomicBool , AtomicU16 } ;
1616use std:: sync:: LazyLock ;
17- use wreq:: redirect:: Policy ;
18- use wreq:: { header as wreq_header, Client as WreqClient , EmulationFactory , Method , Response as WreqResponse } ;
19- use wreq_util:: { Emulation , EmulationOS , EmulationOption } ;
17+ use std:: result:: Result ;
18+
19+ use crate :: dbg_msg;
20+ use crate :: oauth:: { force_refresh_token, token_daemon, Oauth , OauthBackendImpl } ;
21+ use crate :: server:: RequestExt ;
22+ use crate :: utils:: { format_url, Post } ;
2023
2124const REDDIT_URL_BASE : & str = "https://oauth.reddit.com" ;
2225const REDDIT_URL_BASE_HOST : & str = "oauth.reddit.com" ;
@@ -66,21 +69,19 @@ pub fn build_client() -> WreqClient {
6669 . expect ( "Should always be able to build a client" )
6770}
6871
72+ use crate :: server:: { full, Body } ;
73+
6974/// Convert a wreq Response into a hyper Response<Body>.
70- /// This bridge lets the rest of the codebase stay unchanged .
75+ /// wreq and hyper now both use http v1.x, so header types are directly compatible .
7176trait IntoHyperResponse {
7277 async fn into_hyper ( self ) -> Result < Response < Body > , String > ;
7378}
7479
7580impl IntoHyperResponse for wreq:: Response {
7681 async fn into_hyper ( self ) -> Result < Response < Body > , String > {
77- // wreq uses http v1.x; hyper uses http v0.2.x. Convert via primitives.
78- let status_u16 = self . status ( ) . as_u16 ( ) ;
79- let status = hyper:: StatusCode :: from_u16 ( status_u16) . map_err ( |e| e. to_string ( ) ) ?;
82+ let status = self . status ( ) ;
8083
81- // Snapshot headers before consuming self (bytes() moves self).
82- // Use SmallVec-style stack storage: most responses have < 32 headers.
83- let mut builder = hyper:: Response :: builder ( ) . status ( status) ;
84+ let mut builder = hyper:: Response :: builder ( ) . status ( status. as_u16 ( ) ) ;
8485 for ( k, v) in self . headers ( ) {
8586 // Skip headers that hyper rejects (e.g. non-ASCII bytes in CDN headers)
8687 // rather than aborting the entire response.
@@ -100,7 +101,7 @@ impl IntoHyperResponse for wreq::Response {
100101 h. insert ( header:: CONTENT_LENGTH , bytes. len ( ) . into ( ) ) ;
101102 }
102103
103- builder. body ( Body :: from ( bytes) ) . map_err ( |e| e. to_string ( ) )
104+ builder. body ( full ( bytes) ) . map_err ( |e| e. to_string ( ) )
104105 }
105106}
106107
@@ -191,7 +192,7 @@ pub async fn canonical_path(path: String, tries: i8) -> Result<Option<String>, S
191192 }
192193}
193194
194- pub async fn proxy ( req : HyperRequest < Body > , format : & str ) -> Result < HyperResponse < Body > , String > {
195+ pub async fn proxy ( req : Request < Body > , format : & str ) -> Result < Response < Body > , String > {
195196 let mut url = format ! ( "{format}?{}" , req. uri( ) . query( ) . unwrap_or_default( ) ) ;
196197
197198 // For each parameter in request
@@ -221,30 +222,24 @@ pub async fn proxy(req: HyperRequest<Body>, format: &str) -> Result<HyperRespons
221222 // This is needed or Reddit will redirect us to a /media landing page that just renders the image.
222223 builder = builder. header ( wreq_header:: ACCEPT , "*/*" ) ;
223224
224- builder
225- . send ( )
226- . await
227- . map ( |mut res| {
228- let headers = res. headers_mut ( ) ;
229-
230- let mut rm = |key : & str | headers. remove ( key) ;
231-
232- rm ( "access-control-expose-headers" ) ;
233- rm ( "server" ) ;
234- rm ( "vary" ) ;
235- rm ( "etag" ) ;
236- rm ( "x-cdn" ) ;
237- rm ( "x-cdn-client-region" ) ;
238- rm ( "x-cdn-name" ) ;
239- rm ( "x-cdn-server-region" ) ;
240- rm ( "x-reddit-cdn" ) ;
241- rm ( "x-reddit-video-features" ) ;
242- rm ( "Nel" ) ;
243- rm ( "Report-To" ) ;
244-
245- res. into_hyper_response ( )
246- } )
247- . map_err ( |e| e. to_string ( ) )
225+ let mut res = builder. send ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
226+ {
227+ let headers = res. headers_mut ( ) ;
228+ let mut rm = |key : & str | headers. remove ( key) ;
229+ rm ( "access-control-expose-headers" ) ;
230+ rm ( "server" ) ;
231+ rm ( "vary" ) ;
232+ rm ( "etag" ) ;
233+ rm ( "x-cdn" ) ;
234+ rm ( "x-cdn-client-region" ) ;
235+ rm ( "x-cdn-name" ) ;
236+ rm ( "x-cdn-server-region" ) ;
237+ rm ( "x-reddit-cdn" ) ;
238+ rm ( "x-reddit-video-features" ) ;
239+ rm ( "Nel" ) ;
240+ rm ( "Report-To" ) ;
241+ }
242+ res. into_hyper ( ) . await
248243}
249244
250245/// Makes a GET request to Reddit at `path`. By default, this will honor HTTP
@@ -399,13 +394,12 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
399394 None
400395 } ;
401396
402- // asynchronously aggregate the chunks of the body
403- match hyper :: body :: aggregate ( response. into_hyper_response ( ) ) . await {
404- Ok ( body ) => {
405- let has_remaining = body . has_remaining ( ) ;
397+ // Collect the body bytes
398+ match response. collect ( ) . await {
399+ Ok ( collected ) => {
400+ let body_bytes = collected . to_bytes ( ) ;
406401
407- if !has_remaining {
408- // Rate limited, so spawn a force_refresh_token()
402+ if body_bytes. is_empty ( ) {
409403 tokio:: spawn ( force_refresh_token ( ) ) ;
410404 return match reset {
411405 Some ( val) => Err ( format ! (
@@ -416,8 +410,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
416410 } ;
417411 }
418412
419- // Parse the response from Reddit as JSON
420- match serde_json:: from_reader ( body. reader ( ) ) {
413+ match serde_json:: from_slice ( & body_bytes) {
421414 Ok ( value) => {
422415 let json: Value = value;
423416
@@ -513,34 +506,6 @@ pub async fn rate_limit_check() -> Result<(), String> {
513506 Ok ( ( ) )
514507}
515508
516- trait IntoHyperResponse {
517- fn into_hyper_response ( self ) -> HyperResponse < Body > ;
518- }
519-
520- impl IntoHyperResponse for WreqResponse {
521- fn into_hyper_response ( self ) -> HyperResponse < Body > {
522- let status = self . status ( ) ;
523- let version = self . version ( ) ;
524-
525- let mut builder = HyperResponse :: builder ( ) . status ( status. as_u16 ( ) ) . version ( match version {
526- wreq:: Version :: HTTP_09 => hyper:: Version :: HTTP_09 ,
527- wreq:: Version :: HTTP_10 => hyper:: Version :: HTTP_10 ,
528- wreq:: Version :: HTTP_11 => hyper:: Version :: HTTP_11 ,
529- wreq:: Version :: HTTP_2 => hyper:: Version :: HTTP_2 ,
530- wreq:: Version :: HTTP_3 => hyper:: Version :: HTTP_3 ,
531- _ => hyper:: Version :: HTTP_11 ,
532- } ) ;
533-
534- for ( name, value) in self . headers ( ) {
535- builder = builder. header (
536- header:: HeaderName :: from_bytes ( name. as_str ( ) . as_bytes ( ) ) . unwrap ( ) ,
537- header:: HeaderValue :: from_bytes ( value. as_bytes ( ) ) . unwrap ( ) ,
538- ) ;
539- }
540-
541- builder. body ( Body :: wrap_stream ( self . bytes_stream ( ) ) ) . unwrap ( )
542- }
543- }
544509
545510#[ cfg( test) ]
546511mod tests {
0 commit comments