gostd_http/
lib.rs

1//! Package http provides HTTP client and server implementations.
2//!
3//! <details class="rustdoc-toggle top-doc">
4//! <summary class="docblock">zh-cn</summary>
5//! http包提供了HTTP客户端和服务端的实现。
6//! </details>
7
8// #![allow(unused_assignments)]
9#![allow(unused)]
10// #![allow(dead_code)]
11#![allow(non_upper_case_globals)]
12#![allow(non_snake_case)]
13#![allow(non_camel_case_types)]
14
15/// Common HTTP methods.
16#[derive(Copy, Clone)]
17pub enum Method {
18    Get,
19    Head,
20    Post,
21    Put,
22    Patch, // RFC 5789
23    Delete,
24    Connect,
25    Options,
26    Trace,
27}
28
29impl Method {
30    pub fn String(&self) -> &str {
31        match self {
32            Method::Get => "GET",
33            Method::Head => "HEAD",
34            Method::Post => "POST",
35            Method::Put => "PUT",
36            Method::Patch => "PATCH",
37            Method::Delete => "DELETE",
38            Method::Connect => "CONNECT",
39            Method::Options => "OPTHONS",
40            Method::Trace => "TRACE",
41        }
42    }
43}
44
45/// HTTP status codes as registered with IANA. See: <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml>
46pub enum Status {
47    Continue = 100,                      // RFC 7231, 6.2.1
48    SwitchingProtocols = 101,            // RFC 7231, 6.2.2
49    Processing = 102,                    // RFC 2518, 10.1
50    EarlyHints = 103,                    // RFC 8297
51    OK = 200,                            // RFC 7231, 6.3.1
52    Created = 201,                       // RFC 7231, 6.3.2
53    Accepted = 202,                      // RFC 7231, 6.3.3
54    NonAuthoritativeInfo = 203,          // RFC 7231, 6.3.4
55    NoContent = 204,                     // RFC 7231, 6.3.5
56    ResetContent = 205,                  // RFC 7231, 6.3.6
57    PartialContent = 206,                // RFC 7233, 4.1
58    MultiStatus = 207,                   // RFC 4918, 11.1
59    AlreadyReported = 208,               // RFC 5842, 7.1
60    IMUsed = 226,                        // RFC 3229, 10.4.1
61    MultipleChoices = 300,               // RFC 7231, 6.4.1
62    MovedPermanently = 301,              // RFC 7231, 6.4.2
63    Found = 302,                         // RFC 7231, 6.4.3
64    SeeOther = 303,                      // RFC 7231, 6.4.4
65    NotModified = 304,                   // RFC 7232, 4.1
66    UseProxy = 305,                      // RFC 7231, 6.4.5
67    TemporaryRedirect = 307,             // RFC 7231, 6.4.7
68    PermanentRedirect = 308,             // RFC 7538, 3
69    BadRequest = 400,                    // RFC 7231, 6.5.1
70    Unauthorized = 401,                  // RFC 7235, 3.1
71    PaymentRequired = 402,               // RFC 7231, 6.5.2
72    Forbidden = 403,                     // RFC 7231, 6.5.3
73    NotFound = 404,                      // RFC 7231, 6.5.4
74    MethodNotAllowed = 405,              // RFC 7231, 6.5.5
75    NotAcceptable = 406,                 // RFC 7231, 6.5.6
76    ProxyAuthRequired = 407,             // RFC 7235, 3.2
77    RequestTimeout = 408,                // RFC 7231, 6.5.7
78    Conflict = 409,                      // RFC 7231, 6.5.8
79    Gone = 410,                          // RFC 7231, 6.5.9
80    LengthRequired = 411,                // RFC 7231, 6.5.10
81    PreconditionFailed = 412,            // RFC 7232, 4.2
82    RequestEntityTooLarge = 413,         // RFC 7231, 6.5.11
83    RequestURITooLong = 414,             // RFC 7231, 6.5.12
84    UnsupportedMediaType = 415,          // RFC 7231, 6.5.13
85    RequestedRangeNotSatisfiable = 416,  // RFC 7233, 4.4
86    ExpectationFailed = 417,             // RFC 7231, 6.5.14
87    Teapot = 418,                        // RFC 7168, 2.3.3
88    MisdirectedRequest = 421,            // RFC 7540, 9.1.2
89    UnprocessableEntity = 422,           // RFC 4918, 11.2
90    Locked = 423,                        // RFC 4918, 11.3
91    FailedDependency = 424,              // RFC 4918, 11.4
92    TooEarly = 425,                      // RFC 8470, 5.2.
93    UpgradeRequired = 426,               // RFC 7231, 6.5.15
94    PreconditionRequired = 428,          // RFC 6585, 3
95    TooManyRequests = 429,               // RFC 6585, 4
96    RequestHeaderFieldsTooLarge = 431,   // RFC 6585, 5
97    UnavailableForLegalReasons = 451,    // RFC 7725, 3
98    InternalServerError = 500,           //RFC 7231, 6.6.1
99    NotImplemented = 501,                // RFC 7231, 6.6.2
100    BadGateway = 502,                    // RFC 7231, 6.6.3
101    ServiceUnavailable = 503,            // RFC 7231, 6.6.4
102    GatewayTimeout = 504,                // RFC 7231, 6.6.5
103    HTTPVersionNotSupported = 505,       // RFC 7231, 6.6.6
104    VariantAlsoNegotiates = 506,         // RFC 2295, 8.1
105    InsufficientStorage = 507,           // RFC 4918, 11.5
106    LoopDetected = 508,                  // RFC 5842, 7.2
107    NotExtended = 510,                   // RFC 2774, 7
108    NetworkAuthenticationRequired = 511, // RFC 6585, 6
109}
110
111impl Status {
112    pub fn StatusText(&self) -> &str {
113        match self {
114            StatusContinue => "Continue",
115            StatusSwitchingProtocols => "Switching Protocols",
116            StatusProcessing => "Processing",
117            StatusEarlyHints => "Early Hints",
118
119            StatusOK => "OK",
120            StatusCreated => "Created",
121            StatusAccepted => "Accepted",
122            StatusNonAuthoritativeInfo => "Non-Authoritative Information",
123            StatusNoContent => "No Content",
124            StatusResetContent => "Reset Content",
125            StatusPartialContent => "Partial Content",
126            StatusMultiStatus => "Multi-Status",
127            StatusAlreadyReported => "Already Reported",
128            StatusIMUsed => "IM Used",
129
130            StatusMultipleChoices => "Multiple Choices",
131            StatusMovedPermanently => "Moved Permanently",
132            StatusFound => "Found",
133            StatusSeeOther => "See Other",
134            StatusNotModified => "Not Modified",
135            StatusUseProxy => "Use Proxy",
136            StatusTemporaryRedirect => "Temporary Redirect",
137            StatusPermanentRedirect => "Permanent Redirect",
138
139            StatusBadRequest => "Bad Request",
140            StatusUnauthorized => "Unauthorized",
141            StatusPaymentRequired => "Payment Required",
142            StatusForbidden => "Forbidden",
143            StatusNotFound => "Not Found",
144            StatusMethodNotAllowed => "Method Not Allowed",
145            StatusNotAcceptable => "Not Acceptable",
146            StatusProxyAuthRequired => "Proxy Authentication Required",
147            StatusRequestTimeout => "Request Timeout",
148            StatusConflict => "Conflict",
149            StatusGone => "Gone",
150            StatusLengthRequired => "Length Required",
151            StatusPreconditionFailed => "Precondition Failed",
152            StatusRequestEntityTooLarge => "Request Entity Too Large",
153            StatusRequestURITooLong => "Request URI Too Long",
154            StatusUnsupportedMediaType => "Unsupported Media Type",
155            StatusRequestedRangeNotSatisfiable => "Requested Range Not Satisfiable",
156            StatusExpectationFailed => "Expectation Failed",
157            StatusTeapot => "I'm a teapot",
158            StatusMisdirectedRequest => "Misdirected Request",
159            StatusUnprocessableEntity => "Unprocessable Entity",
160            StatusLocked => "Locked",
161            StatusFailedDependency => "Failed Dependency",
162            StatusTooEarly => "Too Early",
163            StatusUpgradeRequired => "Upgrade Required",
164            StatusPreconditionRequired => "Precondition Required",
165            StatusTooManyRequests => "Too Many Requests",
166            StatusRequestHeaderFieldsTooLarge => "Request Header Fields Too Large",
167            StatusUnavailableForLegalReasons => "Unavailable For Legal Reasons",
168
169            StatusInternalServerError => "Internal Server Error",
170            StatusNotImplemented => "Not Implemented",
171            StatusBadGateway => "Bad Gateway",
172            StatusServiceUnavailable => "Service Unavailable",
173            StatusGatewayTimeout => "Gateway Timeout",
174            StatusHTTPVersionNotSupported => "HTTP Version Not Supported",
175            StatusVariantAlsoNegotiates => "Variant Also Negotiates",
176            StatusInsufficientStorage => "Insufficient Storage",
177            StatusLoopDetected => "Loop Detected",
178            StatusNotExtended => "Not Extended",
179            StatusNetworkAuthenticationRequired => "Network Authentication Required",
180        }
181    }
182}
183
184/// DefaultMaxHeaderBytes is the maximum permitted size of the headers in an HTTP request. This can be overridden by setting Server.MaxHeaderBytes.
185use gostd_builtin::*;
186mod error;
187pub use error::{HTTPConnectError, HttpResult};
188const DefaultMaxHeaderBytes: int32 = 1 << 20;
189/// DefaultMaxIdleConnsPerHost is the default value of Transport's MaxIdleConnsPerHost.
190const DefaultMaxIdleConnsPerHost: int32 = 2;
191
192/// TimeFormat is the time format to use when generating times in HTTP headers. It is like time.RFC1123 but hard-codes GMT as the time zone. The time being formatted must be in UTC for Format to generate the correct format.
193///
194/// For parsing this time format, see ParseTime.
195const TimeFormat: &str = "Mon, 02 Jan 2006 15:04:05 GMT";
196
197/// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys that, if present, signals that the map entry is actually for the response trailers, and not the response headers. The prefix is stripped after the ServeHTTP call finishes and the values are sent in the trailers.
198///
199/// This mechanism is intended only for trailers that are not known prior to the headers being written. If the set of trailers is fixed or known before the header is written, the normal Go trailers mechanism is preferred:
200const TrailerPrefix: &str = "Trailer:";
201
202use gostd_io::*;
203use gostd_strings as strings;
204use gostd_time as time;
205use gostd_url as url;
206use std::collections::HashMap;
207use std::collections::HashSet;
208use std::convert::TryInto;
209/// Get issues a GET to the specified URL. If the response is one of the following redirect codes, Get follows the redirect,up to a maximum of 10 redirects:
210/// ```text
211/// 301 (Moved Permanently)
212/// 302 (Found)
213/// 303 (See Other)
214/// 307 (Temporary Redirect)
215/// 308 (Permanent Redirect)
216/// ```
217/// Get is a wrapper around Client.Get.
218/// <details class="rustdoc-toggle top-doc">
219/// <summary class="docblock">zh-cn</summary>
220/// Get向指定的URL发出一个GET请求,如果回应的状态码如下,Get会在调用c.CheckRedirect后执行重定向
221///Get是对包变量Client的Get方法的包装。
222/// </details>
223///
224/// # Example
225///
226/// ```
227/// use gostd_http as http;
228///
229/// fn main() -> Result<(), Box<dyn std::error::Error>> {
230///     let url = "https://petstore.swagger.io/v2/pet/findByStatus?status=available";
231///     let response = http::Get(url)?;///
232///     println!(
233///         "{}",
234///         String::from_utf8(response.Body.expect("return body error").to_vec()).unwrap()
235///     );
236///     Ok(())
237/// }
238/// ```
239pub fn Get(url: &str) -> HttpResult<Response> {
240    Client::New().Get(url)
241}
242
243pub fn Head(url: &str) -> HttpResult<Response> {
244    Client::New().Head(url)
245}
246
247/// Post issues a POST to the specified URL. Post is a wrapper around DefaultClient.Post.
248/// To set custom headers, use Request::New and Client.Do.
249/// <details class="rustdoc-toggle top-doc">
250/// <summary class="docblock">zh-cn</summary>
251/// Post向指定的URL发出一个POST请求。bodyType为POST数据的类型, body为POST数据,作为请求的主体
252/// </details>
253///
254/// # Example
255///
256/// ```
257/// use gostd_http as http;
258/// fn main() -> Result<(), Box<dyn std::error::Error>> {
259///     let url = "https://petstore.swagger.io/v2/pet";
260///     let postbody = r#"{"id":0,"category":{"id":0,"name":"string"},"name":"doggie","photoUrls":["string"],"tags":[{"id":0,"name":"string"}],"status":"available"}"#
261///    .as_bytes()
262///    .to_vec();
263///    let response = http::Post(url, "application/json", Some(postbody.into()))?;
264///
265///    println!(
266///        "{}",
267///        String::from_utf8(response.Body.expect("return body error").to_vec()).unwrap()
268///    );
269///
270///    Ok(())
271/// }
272///
273/// ```
274pub fn Post(url: &str, contentType: &str, body: Option<Bytes>) -> HttpResult<Response> {
275    Client::New().Post(url, contentType, body)
276}
277
278pub fn PostForm(url: &str, data: url::Values) -> HttpResult<Response> {
279    Client::New().PostForm(url, data)
280}
281
282pub fn Patch(url: &str, body: Option<Bytes>) -> HttpResult<Response> {
283    Client::New().Patch(url, body)
284}
285
286pub fn Put(url: &str, body: Option<Bytes>) -> HttpResult<Response> {
287    Client::New().Put(url, body)
288}
289
290pub fn Delete(url: &str) -> HttpResult<Response> {
291    Client::New().Delete(url)
292}
293
294pub struct Client {
295    Transport: Box<dyn RoundTripper>,
296    // CheckRedirect: fn(req: &Request, via: Vec<&Request>) -> Result<(), Error>,
297    Jar: Box<dyn CookieJar>,
298    Timeout: time::Duration,
299}
300impl Default for Client {
301    fn default() -> Self {
302        Self {
303            Transport: Box::new(Transport::default()),
304            Timeout: time::Duration::new(0),
305            Jar: Box::new(Cookie::default()),
306        }
307    }
308}
309impl Client {
310    pub fn New() -> Self {
311        Self::default()
312    }
313
314    pub fn Get(&mut self, url: &str) -> HttpResult<Response> {
315        let mut req = Request::New(Method::Get, url, None)?;
316        self.Do(&mut req)
317    }
318
319    pub fn Post(
320        &mut self,
321        url: &str,
322        contentType: &str,
323        body: Option<Bytes>,
324    ) -> HttpResult<Response> {
325        let mut req = Request::New(Method::Post, url, body)?;
326        req.Header.Set("Content-Type", contentType);
327        self.Do(&mut req)
328    }
329
330    pub fn PostForm(&mut self, url: &str, data: url::Values) -> HttpResult<Response> {
331        self.Post(
332            url,
333            "application/x-www-form-urlencoded",
334            Some(data.Encode().into_bytes().into()),
335        )
336    }
337
338    pub fn Head(&mut self, url: &str) -> HttpResult<Response> {
339        let mut req = Request::New(Method::Head, url, None)?;
340        self.Do(&mut req)
341    }
342
343    pub fn Patch(&mut self, url: &str, body: Option<Bytes>) -> HttpResult<Response> {
344        let mut req = Request::New(Method::Patch, url, body)?;
345        self.Do(&mut req)
346    }
347
348    pub fn Put(&mut self, url: &str, body: Option<Bytes>) -> HttpResult<Response> {
349        let mut req = Request::New(Method::Put, url, body)?;
350        self.Do(&mut req)
351    }
352
353    pub fn Delete(&mut self, url: &str) -> HttpResult<Response> {
354        let mut req = Request::New(Method::Delete, url, None)?;
355        self.Do(&mut req)
356    }
357
358    pub fn Do(&mut self, req: &mut Request) -> HttpResult<Response> {
359        self.done(req)
360    }
361
362    fn send(
363        &mut self,
364        req: &mut Request,
365        deadline: time::Time,
366    ) -> HttpResult<(Response, fn() -> bool)> {
367        let (resp, didTimeout) = send(req, self.transport(), deadline)?;
368        Ok((resp, didTimeout))
369    }
370
371    fn done(&mut self, req: &mut Request) -> HttpResult<Response> {
372        let deadline = self.deadline();
373        let (resp, didTimeout) = self.send(req, deadline)?;
374        Ok(resp)
375    }
376
377    fn deadline(&mut self) -> time::Time {
378        if self.Timeout > time::Duration::new(0) {
379            return time::Now().Add(&self.Timeout);
380        }
381        time::Time::default()
382    }
383
384    fn transport(&self) -> Box<dyn RoundTripper> {
385        Box::new(Transport::default())
386    }
387}
388
389fn send(
390    ireq: &mut Request,
391    mut rt: Box<dyn RoundTripper>,
392    deadline: time::Time,
393) -> HttpResult<(Response, fn() -> bool)> {
394    let mut resp = Response::default();
395    fn didTimeout() -> bool {
396        return false;
397    };
398    loop {
399        let mut resp = rt.RoundTrip(ireq)?;
400        let mut loc = resp.Header.Get("Location");
401        let (redirectMethod, shouldRedirect, includeBody) =
402            redirectBehavior(ireq.Method.as_str(), &resp, ireq);
403        if !shouldRedirect {
404            return Ok((resp, didTimeout));
405        }
406        let mut u = ireq.URL.Parse(loc.as_str())?;
407        let urlRef = refererForURL(&ireq.URL, &u);
408        ireq.Method = redirectMethod.clone();
409        ireq.URL = u.clone();
410        ireq.Header.Set("Referer", urlRef.as_str());
411    }
412}
413
414fn redirectBehavior(reqMethod: &str, resp: &Response, ireq: &Request) -> (String, bool, bool) {
415    let mut shouldRedirect = false;
416    let mut includeBody = false;
417    match resp.StatusCode {
418        301 | 302 | 303 => return (Method::Get.String().to_string(), true, false),
419        307 | 308 => {
420            if resp.Header.Get("Location") == "" {
421                shouldRedirect = true;
422                includeBody = false;
423            }
424            if ireq.Body.is_none() && ireq.ContentLength != 0 {
425                shouldRedirect = false;
426            }
427        }
428        _ => (),
429    }
430    (reqMethod.to_string(), shouldRedirect, includeBody)
431}
432
433pub trait RoundTripper {
434    fn RoundTrip(&mut self, r: &Request) -> HttpResult<Response>;
435}
436
437fn refererForURL(lastReq: &url::URL, newReq: &url::URL) -> String {
438    if (lastReq.Scheme == "https") && (newReq.Scheme == "http") {
439        return "".to_string();
440    }
441    let mut referer = lastReq.String();
442    if let Some(user) = lastReq.User.clone() {
443        return referer;
444    }
445    let auth = "@";
446    referer = strings::Replace(referer.as_str(), auth, "", 1);
447    referer
448}
449
450#[derive(Default, Clone, Debug)]
451pub struct Request {
452    Method: String,
453    URL: url::URL,
454    Proto: String,
455    ProtoMajor: int,
456    ProtoMinor: int,
457    pub Header: Header,
458    pub Body: Option<Bytes>,
459    // GetBody func() (io.ReadCloser, error)
460    ContentLength: int64,
461    TransferEncoding: Vec<String>,
462    Close: bool,
463    Host: String,
464    Form: url::Values,
465    PostForm: url::Values,
466    // MultipartForm:*multipart.Form,
467    Trailer: Header,
468    RemoteAddr: String,
469    RequestURI: String,
470    isTLS: bool,
471    // TLS *tls.ConnectionState,
472    // Cancel <-chan struct{}
473    // ctx context.Context
474}
475
476impl Request {
477    pub fn New(method: Method, url: &str, body: Option<Bytes>) -> HttpResult<Request> {
478        let mut u = url::Parse(url)?;
479
480        u.Host = removeEmptyPort(u.Host.as_str()).to_string();
481        let mut req = Request {
482            Method: method.String().to_owned(),
483            URL: u.clone(),
484            Proto: "HTTP/1.1".to_string(),
485            ProtoMajor: 1,
486            ProtoMinor: 1,
487            Header: Header::default(),
488            ContentLength: 0,
489            TransferEncoding: Vec::<String>::new(),
490            Close: false,
491            Form: url::Values::default(),
492            PostForm: url::Values::default(),
493            Trailer: Header::default(),
494            RemoteAddr: "".to_string(),
495            RequestURI: "".to_string(),
496            Body: None,
497            Host: u.Host.to_owned(),
498            isTLS: false,
499        };
500        if let Some(buf) = body {
501            req.ContentLength = len!(buf) as i64;
502
503            req.Body = Some(buf);
504        }
505        if strings::HasPrefix(url, "https://") {
506            req.isTLS = true
507        }
508        Ok(req)
509    }
510
511    pub fn Write(&self) -> HttpResult<Vec<u8>> {
512        self.write(false)
513    }
514
515    fn write(&self, usingProxy: bool) -> HttpResult<Vec<u8>> {
516        let mut buf = strings::Builder::new();
517        let host = self.Host.clone();
518        let ruri = self.URL.RequestURI();
519        let userAgent = "rust-http-client/1.1";
520        buf.WriteString(format!("{} {} HTTP/1.1\r\n", self.Method.as_str(), ruri).as_str());
521        buf.WriteString(format!("Host: {}\r\n", host).as_str());
522        buf.WriteString(format!("User-Agent: {}\r\n", userAgent).as_str());
523        buf.WriteString(self.writeHeader().as_str());
524        buf.WriteString("\r\n");
525        if let Some(body) = &self.Body {
526            buf.Write(body.to_vec())?;
527            buf.WriteString("\r\n");
528        }
529        Ok(buf.Bytes())
530    }
531
532    fn writeHeader(&self) -> String {
533        let mut buf = strings::Builder::new();
534        for (k, v) in &self.Header.0 {
535            if len!(v) > 1 {
536                let value = strings::Join(v.iter().map(|x| x.as_str()).collect(), ",");
537                buf.WriteString(format!("{}: {}\r\n", k.as_str(), value.as_str()).as_str());
538            } else {
539                buf.WriteString(format!("{}: {}\r\n", k.as_str(), v[0].as_str()).as_str());
540            }
541        }
542        if self.ContentLength > 0 {
543            buf.WriteString(format!("Content-Length: {}\r\n", self.ContentLength).as_str());
544        }
545        buf.String()
546    }
547}
548#[derive(Default, Debug, Clone)]
549pub struct Response {
550    pub Status: String,
551    pub StatusCode: int,
552    pub Proto: String,
553    pub ProtoMajor: int,
554    pub ProtoMinor: int,
555    pub Header: Header,
556    pub ContentLength: int64,
557    pub TransferEncoding: Vec<String>,
558    pub Body: Option<BytesMut>,
559    pub Close: bool,
560    pub Uncompressed: bool,
561    pub Trailer: Header,
562    pub Request: Request,
563}
564
565impl Response {
566    pub fn Cookies(&self) -> Vec<Cookie> {
567        readSetCookies(&self.Header)
568    }
569}
570fn isCookieNameValid(raw: &str) -> bool {
571    if raw == "" {
572        return false;
573    }
574    strings::IndexFunc(raw, isNotToken) < 0
575}
576
577fn isNotToken(r: rune) -> bool {
578    !validHeaderFieldByte(r as u8)
579}
580
581fn validCookieValueByte(b: byte) -> bool {
582    return 0x20 <= b && b < 0x7f && b != b'"' && b != b';' && b != b'\\';
583}
584
585fn parseCookieValue(mut raw: &str, allowDoubleQuote: bool) -> (string, bool) {
586    // Strip the quotes, if present.
587    if allowDoubleQuote
588        && len!(raw) > 1
589        && raw.bytes().nth(0) == Some(b'"')
590        && raw.bytes().nth((len!(raw) - 1)) == Some(b'"')
591    {
592        raw = &raw[1..len!(raw) - 1]
593    }
594    for i in 0..len!(raw) {
595        if !validCookieValueByte(raw.as_bytes()[i as usize]) {
596            return ("".to_string(), false);
597        }
598    }
599    return (raw.to_string(), true);
600}
601fn readSetCookies(h: &Header) -> Vec<Cookie> {
602    let cookieCount = len!(h.0.get(&"Set-Cookie".to_string()).unwrap());
603    if cookieCount == 0 {
604        return vec![];
605    }
606    let mut cookies = Vec::with_capacity(cookieCount);
607    for line in h.0.get("Set-Cookie").unwrap() {
608        let mut parts = strings::Split(strings::TrimSpace(line.as_str()), ";");
609        if len!(parts) == 1 && parts[0] == "" {
610            continue;
611        }
612        parts[0] = strings::TrimSpace(parts[0]);
613
614        let j = strings::Index(parts[0], "=");
615        if j < 0 {
616            continue;
617        }
618        let mut name = &parts[0][..j as usize];
619        let mut value = &parts[0][j as usize + 1..];
620        if !isCookieNameValid(name) {
621            continue;
622        }
623        let cookie = parseCookieValue(value, true);
624        value = &cookie.0;
625        let ok = &cookie.1;
626        if !ok {
627            continue;
628        }
629        let mut c = Cookie::default();
630        c.Name = name.to_string();
631        c.Value = value.to_string();
632        c.Raw = line.to_string();
633
634        for i in 1..len!(parts) {
635            parts[i] = strings::TrimSpace(parts[i]);
636            if len!(parts[i]) == 0 {
637                continue;
638            }
639            let mut attr = parts[i];
640            let mut val = "";
641            let j = strings::Index(attr, "=");
642            if j >= 0 {
643                attr = &attr[..j as usize];
644                val = &attr[j as usize + 1..];
645            }
646            if !attr.is_ascii() {
647                continue;
648            }
649
650            let cok = parseCookieValue(val, false);
651            val = &cok.0;
652            let ok = &cok.1;
653            if !ok {
654                c.Unparsed.push(parts[i].to_string());
655                continue;
656            }
657            let lowerAttr = strings::ToLower(attr);
658            match lowerAttr.as_str() {
659                "sameste" => {
660                    if !val.is_ascii() {
661                        c.SameSite = SameSite::SameSiteDefaultMode;
662                        continue;
663                    }
664                    let lowerVal = strings::ToLower(val);
665                    match lowerVal.as_str() {
666                        "lax" => c.SameSite = SameSite::SameSiteLaxMode,
667                        "strict" => c.SameSite = SameSite::SameSiteStrictMode,
668                        "none" => c.SameSite = SameSite::SameSiteNoneModepub,
669                        _ => c.SameSite = SameSite::SameSiteDefaultMode,
670                    }
671                    continue;
672                }
673                "secure" => {
674                    c.Secure = true;
675                    continue;
676                }
677                "httponly" => {
678                    c.HttpOnly = true;
679                    continue;
680                }
681                "domain" => {
682                    c.Domain = val.to_string();
683                    continue;
684                }
685                "max-age" => {
686                    let mut secs: int = 0;
687                    let res = val.parse::<int>();
688                    if res.is_err() || (secs != 0 && val.bytes().nth(0) == Some(b'0')) {
689                        continue;
690                    }
691                    secs = res.unwrap();
692                    if secs <= 0 {
693                        secs = -1;
694                    }
695                    c.MaxAge = secs;
696                    continue;
697                }
698                "expires" => {
699                    c.RawExpires = val.to_string();
700                    if let Ok(mut exptime) = time::Parse(time::RFC1123, val) {
701                        c.Expires = exptime.UTC();
702                    } else {
703                        if let Ok(mut exptime) = time::Parse("Mon, 02-Jan-2006 15:04:05 MST", val) {
704                            c.Expires = exptime.UTC();
705                        } else {
706                            c.Expires = time::Time::default();
707                            continue;
708                        }
709                    }
710                    continue;
711                }
712                "path" => {
713                    c.Path = val.to_string();
714                    continue;
715                }
716                _ => (),
717            }
718            c.Unparsed.push(parts[i].to_string());
719        }
720        cookies.push(c);
721    }
722    cookies
723}
724
725#[derive(PartialEq, PartialOrd, Debug, Clone)]
726pub enum SameSite {
727    SameSiteDefaultMode,
728    SameSiteLaxMode,
729    SameSiteStrictMode,
730    SameSiteNoneModepub,
731}
732
733impl Default for SameSite {
734    fn default() -> Self {
735        SameSite::SameSiteDefaultMode
736    }
737}
738trait CookieJar {
739    fn SetCookies(&mut self, u: &url::URL, cookies: Vec<Cookie>);
740
741    fn Cookies(&self, u: &url::URL) -> Vec<Cookie>;
742}
743
744#[derive(Default, PartialEq, Debug, Clone)]
745pub struct Header(HashMap<String, Vec<String>>);
746
747impl Header {
748    pub fn NewWithHashMap(m: HashMap<String, Vec<String>>) -> Header {
749        Header(m)
750    }
751    pub fn Add(&mut self, key: &str, value: &str) {
752        self.0
753            .get_mut(&key.to_string())
754            .unwrap()
755            .push(value.to_string())
756    }
757
758    pub fn Set(&mut self, key: &str, value: &str) {
759        self.0.insert(key.to_string(), vec![value.to_string()]);
760    }
761
762    pub fn Get(&self, key: &str) -> String {
763        self.0
764            .get(key)
765            .unwrap_or(&vec!["".to_string()])
766            .get(0)
767            .unwrap()
768            .to_string()
769    }
770}
771
772#[derive(Default, PartialEq, PartialOrd, Debug, Clone)]
773pub struct Cookie {
774    Name: String,
775    Value: String,
776    Path: String,        // optional
777    Domain: String,      // optional
778    Expires: time::Time, // optional
779    RawExpires: String,  // for reading cookies only
780
781    // MaxAge=0 means no 'Max-Age' attribute specified.
782    // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
783    // MaxAge>0 means Max-Age attribute present and given in seconds
784    MaxAge: int,
785    Secure: bool,
786    HttpOnly: bool,
787    SameSite: SameSite,
788    Raw: String,
789    Unparsed: Vec<String>, // Raw text of unparsed attribute-value pairs
790}
791
792impl CookieJar for Cookie {
793    fn SetCookies(&mut self, u: &url::URL, cookies: Vec<Cookie>) {
794        todo!()
795    }
796
797    fn Cookies(&self, u: &url::URL) -> Vec<Cookie> {
798        todo!()
799    }
800}
801// SameSite allows a server to define a cookie attribute making it impossible for
802// the browser to send this cookie along with cross-site requests. The main
803// goal is to mitigate the risk of cross-origin information leakage, and provide
804// some protection against cross-site request forgery attacks.
805//
806// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
807// type SameSite = int;
808
809fn hasPort(s: &str) -> bool {
810    strings::LastIndex(s, ":") > strings::LastIndex(s, "]")
811}
812
813// removeEmptyPort strips the empty port in ":port" to ""
814// as mandated by RFC 3986 Section 6.2.3.
815fn removeEmptyPort(host: &str) -> &str {
816    if hasPort(host) {
817        return strings::TrimSuffix(host, ":");
818    }
819    host
820}
821use std::iter::FromIterator;
822use std::sync;
823#[derive(Default, Clone)]
824struct Transport {
825    // idleMu: sync::Mutex,
826    closeIdle: bool,
827    // idleConn:HashMap<String, Vec<>>
828    Proxy: Option<url::URL>,
829    // Dial: fn(network: &str, addr: &str) -> Result<net::TcpConn, Error>,
830    ForceAttemptHTTP2: bool,
831    MaxIdleConns: int,
832    // IdleConnTimeout:       90 * time.Second,
833    // TLSHandshakeTimeout:   10 * time.Second,
834    // ExpectContinueTimeout: 1 * time.Second,
835    DisableKeepAlives: bool,
836
837    DisableCompression: bool,
838    iMaxIdleConnsPerHost: int,
839    MaxConnsPerHost: int,
840    MaxResponseHeaderBytes: int64,
841    WriteBufferSize: int,
842    ReadBufferSize: int,
843    tlsNextProtoWasNil: bool,
844}
845
846use std::net;
847use std::sync::mpsc;
848impl RoundTripper for Transport {
849    fn RoundTrip(&mut self, req: &Request) -> HttpResult<Response> {
850        self.roundTrip(req)
851    }
852}
853impl Transport {
854    fn roundTrip(&mut self, req: &Request) -> HttpResult<Response> {
855        let treq = &mut transportRequest {
856            Req: req.clone(),
857            extra: None,
858        };
859        let cm = self.connectMethodForRequest(treq)?;
860        let (mut pconn, mut conn) = self.getConn(treq, cm)?;
861        // conn.set_write_timeout(Some(std::time::Duration::new(5, 0)));
862        // conn.set_read_timeout(Some(std::time::Duration::new(5, 0)));
863
864        pconn.roundTrip(treq, conn)
865    }
866
867    fn getConn(
868        &mut self,
869        treq: &transportRequest,
870        cm: connectMethod,
871    ) -> HttpResult<(persistConn, TcpConn)> {
872        let conn = self.dialConn(cm)?;
873        let pconn = persistConn::default();
874        Ok((pconn, conn))
875    }
876
877    fn dialConn(&mut self, cm: connectMethod) -> HttpResult<TcpConn> {
878        // pconn.t = self;
879        // pconn.reqch = mpsc::channel();
880        // pconn.writech = mpsc::channel();
881        // pconn.writeLoopDone = mpsc::channel();
882        self.dial("tcp", cm.addr().as_str())
883        // pconn.conn = conn;
884        // pconn.br = bufio::NewReaderSize(pconn, self.readBufferSize());
885        // pconn.bw = bufio::NewWriterSize(persistConnWriter { pconn }, self.writeBufferSize());
886        // 待实现读写进程
887        /* go pconn.readLoop()
888        go pconn.writeLoop() */
889        // Ok(pconn)
890    }
891
892    fn dial(&mut self, network: &str, addr: &str) -> HttpResult<TcpConn> {
893        Ok(net::TcpStream::connect(addr)?)
894    }
895
896    fn connectMethodForRequest(&mut self, treq: &transportRequest) -> HttpResult<connectMethod> {
897        let mut cm = connectMethod::default();
898        cm.targetScheme = treq.Req.URL.Scheme.clone();
899        cm.targetAddr = canonicalAddr(&treq.Req.URL.clone());
900        cm.proxyURL = None;
901        cm.onlyH1 = true; //待优化
902        Ok(cm)
903    }
904
905    fn wirteBufferSize(self) -> int {
906        if self.WriteBufferSize > 0 {
907            return self.WriteBufferSize;
908        }
909        4 << 10
910    }
911
912    fn readBufferSize(self) -> int {
913        if self.ReadBufferSize > 0 {
914            return self.ReadBufferSize;
915        }
916        4 << 10
917    }
918}
919
920fn canonicalAddr(url: &url::URL) -> String {
921    let portMap: HashMap<String, String> = [
922        ("http".to_string(), "80".to_string()),
923        ("https".to_string(), "443".to_string()),
924        ("socks5".to_string(), "1080".to_string()),
925    ]
926    .iter()
927    .cloned()
928    .collect();
929    let addr = url.Hostname().to_string();
930    let mut port = url.Port().to_string();
931    if port == "" {
932        port = portMap.get(url.Scheme.as_str()).unwrap().to_string();
933    }
934    strings::Join(vec![addr.as_str(), port.as_str()], ":")
935}
936
937#[derive(Default, Clone)]
938struct transportRequest {
939    Req: Request,
940    extra: Option<Header>,
941}
942
943impl transportRequest {
944    fn extraHeaders(&mut self) -> Header {
945        if let Some(extra) = self.extra.clone() {
946            return extra;
947        }
948        Header::default()
949    }
950}
951
952#[derive(Default, PartialEq, PartialOrd, Clone)]
953struct connectMethod {
954    proxyURL: Option<url::URL>, // nil for no proxy, else full proxy URL
955    targetScheme: String,       // "http" or "https"
956    // If proxyURL specifies an http or https proxy, and targetScheme is http (not https),
957    // then targetAddr is not included in the connect method key, because the socket can
958    // be reused for different targetAddr values.
959    targetAddr: String,
960    onlyH1: bool, // whether to disable HTTP/2 and force HTTP/1
961}
962impl connectMethod {
963    fn scheme(&self) -> String {
964        self.targetScheme.clone()
965    }
966
967    fn addr(&self) -> String {
968        self.targetAddr.clone()
969    }
970}
971type TcpConn = TcpStream;
972use std::sync::mpsc::channel;
973#[derive(Default, Clone)]
974struct persistConn {
975    t: Transport,
976    // br: bufio.Reader,
977    // bw: bufio.Writer,
978    nwrite: int64,
979    // reqch: channel,
980    // writech: channel,
981    isProxy: bool,
982    sawEOF: bool,
983    readLimit: int64,
984    // writeErrch: channel,
985    // writeLoopDone: channel,
986    numExpectedResponses: int,
987    broken: bool,
988    reused: bool,
989}
990
991use bytes::Bytes;
992use rustls::ClientConnection;
993use rustls::StreamOwned;
994use std::convert::TryFrom;
995use std::io::prelude::*;
996use std::io::BufReader;
997use std::net::Shutdown;
998use std::net::TcpStream;
999use std::rc::Rc;
1000use std::sync::Arc;
1001use webpki_roots::TLS_SERVER_ROOTS;
1002impl persistConn {
1003    fn roundTrip(&mut self, req: &mut transportRequest, mut conn: TcpConn) -> HttpResult<Response> {
1004        self.numExpectedResponses += 1;
1005        let mut requestedGzip = false;
1006        if !self.t.DisableCompression
1007            && req.Req.Header.Get("Accept-Encoding") == ""
1008            && req.Req.Header.Get("Range") == ""
1009            && req.Req.Method != "HEAD".to_string()
1010        {
1011            requestedGzip = true;
1012            // req.extra = Some(req.Req.Header.clone());
1013            // let mut hd = req.extra.take().unwrap();
1014            // // hd.Set("Accept-Encoding", "gzip");
1015
1016            // req.extra = Some(hd.clone());
1017            // req.Req.Header = hd;
1018        }
1019        if req.Req.Close {
1020            req.Req.Header.Set("Connection", "close");
1021        }
1022
1023        let r = req.Req.Write()?;
1024
1025        if req.Req.isTLS {
1026            let mut tlsConn = getTLSConn(req.Req.Host.as_str(), conn)?;
1027            tlsConn.write(r.as_slice())?;
1028            let mut reader = BufReader::new(tlsConn);
1029            let resp = ReadResponse(reader, &req.Req)?;
1030            Ok(resp)
1031        } else {
1032            conn.write(r.as_slice())?;
1033            let mut reader = BufReader::new(conn);
1034            let resp = ReadResponse(reader, &req.Req)?;
1035            Ok(resp)
1036        }
1037    }
1038}
1039use bytes::{Buf, BytesMut};
1040use rustls::pki_types::ServerName;
1041use rustls::{ClientConfig, RootCertStore};
1042use std::io::ErrorKind;
1043
1044fn get_tls_config() -> Arc<ClientConfig> {
1045    let mut clientRootCert = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned());
1046
1047    Arc::new(
1048        ClientConfig::builder()
1049            .with_root_certificates(clientRootCert)
1050            .with_no_client_auth(),
1051    )
1052}
1053
1054fn getTLSConn(
1055    dnsName: &str,
1056    socket: TcpConn,
1057) -> HttpResult<StreamOwned<ClientConnection, TcpConn>> {
1058    let tlsconfig = get_tls_config();
1059    let serverName = ServerName::try_from(dnsName.to_owned())?;
1060    let mut tlsClient = ClientConnection::new(tlsconfig, serverName)?;
1061    let mut tlsConn = StreamOwned::new(tlsClient, socket);
1062    Ok(tlsConn)
1063}
1064
1065pub fn ReadResponse(mut r: impl BufRead, req: &Request) -> HttpResult<Response> {
1066    let mut resp = Response::default();
1067    resp.Request = req.clone();
1068    // parse status line。
1069    let mut line = String::new();
1070    r.read_line(&mut line)?;
1071    let parts: Vec<&str> = line.split_whitespace().collect();
1072    if parts.len() < 3 {
1073        return Err(HTTPConnectError::ConnectionFailure(
1074            "malformed HTTP response".to_string(),
1075        ));
1076    }
1077    resp.Proto = parts[0].to_string();
1078    resp.Status = parts[1..].join(" ");
1079    resp.StatusCode = parts[1].parse::<isize>()?;
1080    let vers = ParseHTTPVersion(&resp.Proto);
1081    let ok = vers.2;
1082    if !ok {
1083        return Err(HTTPConnectError::ConnectionFailure(
1084            "malformed HTTP version".to_string(),
1085        ));
1086    }
1087    resp.ProtoMajor = vers.0;
1088    resp.ProtoMinor = vers.1;
1089
1090    // 1. 获取response的header部分,到第一个 '\r\b'独立行为header的结束。
1091    let mut headPart = BytesMut::new();
1092    let mut head_line = String::new();
1093    while r.read_line(&mut head_line).is_ok() {
1094        if head_line.as_bytes() == b"\r\n" {
1095            break;
1096        }
1097        headPart.extend_from_slice(head_line.as_bytes());
1098        head_line.clear()
1099    }
1100
1101    // parse headPart
1102    resp.Header = Header::NewWithHashMap(parseHeader(&headPart)?);
1103    fixPragmaCacheControl(&mut resp.Header);
1104
1105    // set Body
1106    if resp.Header.Get("Transfer-Encoding").as_str() == "chunked" {
1107        // 2.chunked方式传输方式。获取body数据。
1108        resp.Body = Some(parseChunkedBody(r)?);
1109    } else {
1110        // 3. 除chunked外的其他传输方式,都有Content-Length字段,根据长度获取body
1111        let ln: usize = resp
1112            .Header
1113            .Get("Content-Length")
1114            .as_str()
1115            .parse::<usize>()
1116            .expect("Content-Length is not exist");
1117
1118        let mut buf = vec![0; ln]; // 生成固定长度的数组,用于读取定长数据;
1119        r.read_exact(&mut buf)?;
1120        resp.Body = Some(BytesMut::from(&buf[..]));
1121    }
1122    resp.ContentLength = resp.Body.as_ref().map_or(0, |b| b.len() as i64);
1123    Ok(resp)
1124}
1125
1126// chunk数据是以16位数据长度 7acc\r\n独立行开头+ [data] 下一行以\r\n结尾数据段形式,所以数据的结尾用0\r\n表示。
1127fn parseChunkedBody(mut r: impl BufRead) -> HttpResult<BytesMut> {
1128    let mut body = BytesMut::new();
1129    let mut size_buf = vec![];
1130    while r.read_until(b'\n', &mut size_buf).is_ok() {
1131        // 校验开头行是\r\n结尾的chuank size行
1132        if size_buf.ends_with(b"\r\n") {
1133            // 删除尾部的\r\n,只保留表示大小的字符串
1134            size_buf.truncate(size_buf.len() - 2); // Remove "\r\n"
1135
1136            // 16进制chunk大小字符串
1137            let size_str = std::str::from_utf8(&size_buf)?;
1138
1139            // 如果字符串等于"0",已经到最后一个chunk数据段。
1140            if size_str == "0" {
1141                break;
1142            }
1143
1144            // 按chunk长度读取分段的实际数据
1145            let chunk_size = usize::from_str_radix(size_str, 16)?;
1146
1147            let mut chunk_data = vec![0u8; chunk_size];
1148            r.read_exact(&mut chunk_data)?;
1149            body.extend_from_slice(&chunk_data);
1150            //读取每个chunk data 结尾的\r\n,并丢弃掉
1151            let mut crlf = [0u8; 2];
1152            r.read_exact(&mut crlf)?;
1153            //chuank size 行的数据要清空
1154            size_buf.clear();
1155        }
1156    }
1157    Ok(body)
1158}
1159
1160pub type MIMEHeader = HashMap<String, Vec<String>>;
1161
1162fn fixPragmaCacheControl(header: &mut Header) {
1163    if let Some(hp) = header.0.get("Pragma") {
1164        if len!(hp) > 0 && &hp[0] == "no-cache" && header.0.get("Cache-Control").is_none() {
1165            header.Set("Cache-Control", "no-cache");
1166        }
1167    }
1168}
1169
1170fn parseHeader(headPart: &[u8]) -> HttpResult<MIMEHeader> {
1171    let mut m: MIMEHeader = HashMap::new();
1172    let lines = std::str::from_utf8(headPart)?;
1173
1174    for kv in lines.split("\r\n") {
1175        if let Some((key, value)) = kv.split_once(':') {
1176            let key = canonicalMIMEHeaderKey(key);
1177            if key.is_empty() {
1178                continue;
1179            }
1180
1181            let value = value
1182                .trim_start_matches(|c: char| c == ' ' || c == '\t')
1183                .trim_matches('"')
1184                .to_string();
1185
1186            m.entry(key).or_insert_with(Vec::new).push(value);
1187        }
1188    }
1189    Ok(m)
1190}
1191
1192fn startIndexOfBody(response: &Vec<u8>) -> Option<usize> {
1193    let mut sep: Vec<u8> = vec![];
1194    for (i, b) in response.iter().map(|&x| x).enumerate() {
1195        if b == b'\r' || b == b'\n' {
1196            sep.push(b);
1197        } else {
1198            sep.clear();
1199        }
1200        if sep.as_slice() == b"\r\n\r\n" {
1201            return Some(i);
1202        }
1203    }
1204    None
1205}
1206
1207fn validHeaderFieldByte(b: byte) -> bool {
1208    let isTokenTable: HashSet<char> = [
1209        '!', '#', '$', '%', '&', '\'', '*', '+', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8',
1210        '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
1211        'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f',
1212        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
1213        'y', 'z', '|', '~',
1214    ]
1215    .iter()
1216    .cloned()
1217    .collect();
1218
1219    isTokenTable.contains(&(b as char))
1220}
1221// Header KE规范化 content-length|CONTENT-LENGTH => Content-Length
1222const toLower: byte = (b'a' - b'A');
1223fn canonicalMIMEHeaderKey(s: &str) -> String {
1224    let mut upper = true;
1225    let mut new = String::with_capacity(s.len());
1226    for &byte in s.as_bytes() {
1227        let c = if upper && byte >= b'a' && byte <= b'z' {
1228            byte - toLower
1229        } else if !upper && byte >= b'A' && byte <= b'Z' {
1230            byte + toLower
1231        } else {
1232            byte
1233        };
1234        upper = c == b'-';
1235        new.push(c as char);
1236    }
1237    new
1238}
1239
1240pub fn ParseHTTPVersion(vers: &str) -> (int, int, bool) {
1241    let big: int = 1_000_000;
1242
1243    if !vers.starts_with("HTTP/") {
1244        return (0, 0, false);
1245    }
1246
1247    let version_part = &vers[5..];
1248    let parts: Vec<&str> = version_part.split('.').collect();
1249
1250    if parts.len() != 2 {
1251        return (0, 0, false);
1252    }
1253
1254    match (parts[0].parse::<int>(), parts[1].parse::<int>()) {
1255        (Ok(major), Ok(minor)) if major >= 0 && major <= big && minor >= 0 && minor <= big => {
1256            (major, minor, true)
1257        }
1258        _ => (0, 0, false),
1259    }
1260}