1#![allow(unused)]
10#![allow(non_upper_case_globals)]
12#![allow(non_snake_case)]
13#![allow(non_camel_case_types)]
14
15#[derive(Copy, Clone)]
17pub enum Method {
18 Get,
19 Head,
20 Post,
21 Put,
22 Patch, 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
45pub enum Status {
47 Continue = 100, SwitchingProtocols = 101, Processing = 102, EarlyHints = 103, OK = 200, Created = 201, Accepted = 202, NonAuthoritativeInfo = 203, NoContent = 204, ResetContent = 205, PartialContent = 206, MultiStatus = 207, AlreadyReported = 208, IMUsed = 226, MultipleChoices = 300, MovedPermanently = 301, Found = 302, SeeOther = 303, NotModified = 304, UseProxy = 305, TemporaryRedirect = 307, PermanentRedirect = 308, BadRequest = 400, Unauthorized = 401, PaymentRequired = 402, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405, NotAcceptable = 406, ProxyAuthRequired = 407, RequestTimeout = 408, Conflict = 409, Gone = 410, LengthRequired = 411, PreconditionFailed = 412, RequestEntityTooLarge = 413, RequestURITooLong = 414, UnsupportedMediaType = 415, RequestedRangeNotSatisfiable = 416, ExpectationFailed = 417, Teapot = 418, MisdirectedRequest = 421, UnprocessableEntity = 422, Locked = 423, FailedDependency = 424, TooEarly = 425, UpgradeRequired = 426, PreconditionRequired = 428, TooManyRequests = 429, RequestHeaderFieldsTooLarge = 431, UnavailableForLegalReasons = 451, InternalServerError = 500, NotImplemented = 501, BadGateway = 502, ServiceUnavailable = 503, GatewayTimeout = 504, HTTPVersionNotSupported = 505, VariantAlsoNegotiates = 506, InsufficientStorage = 507, LoopDetected = 508, NotExtended = 510, NetworkAuthenticationRequired = 511, }
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
184use gostd_builtin::*;
186mod error;
187pub use error::{HTTPConnectError, HttpResult};
188const DefaultMaxHeaderBytes: int32 = 1 << 20;
189const DefaultMaxIdleConnsPerHost: int32 = 2;
191
192const TimeFormat: &str = "Mon, 02 Jan 2006 15:04:05 GMT";
196
197const 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;
209pub 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
247pub 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 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 ContentLength: int64,
461 TransferEncoding: Vec<String>,
462 Close: bool,
463 Host: String,
464 Form: url::Values,
465 PostForm: url::Values,
466 Trailer: Header,
468 RemoteAddr: String,
469 RequestURI: String,
470 isTLS: bool,
471 }
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 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, Domain: String, Expires: time::Time, RawExpires: String, MaxAge: int,
785 Secure: bool,
786 HttpOnly: bool,
787 SameSite: SameSite,
788 Raw: String,
789 Unparsed: Vec<String>, }
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}
801fn hasPort(s: &str) -> bool {
810 strings::LastIndex(s, ":") > strings::LastIndex(s, "]")
811}
812
813fn 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 closeIdle: bool,
827 Proxy: Option<url::URL>,
829 ForceAttemptHTTP2: bool,
831 MaxIdleConns: int,
832 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 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 self.dial("tcp", cm.addr().as_str())
883 }
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; 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>, targetScheme: String, targetAddr: String,
960 onlyH1: bool, }
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 nwrite: int64,
979 isProxy: bool,
982 sawEOF: bool,
983 readLimit: int64,
984 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 }
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 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 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 resp.Header = Header::NewWithHashMap(parseHeader(&headPart)?);
1103 fixPragmaCacheControl(&mut resp.Header);
1104
1105 if resp.Header.Get("Transfer-Encoding").as_str() == "chunked" {
1107 resp.Body = Some(parseChunkedBody(r)?);
1109 } else {
1110 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]; 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
1126fn 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 if size_buf.ends_with(b"\r\n") {
1133 size_buf.truncate(size_buf.len() - 2); let size_str = std::str::from_utf8(&size_buf)?;
1138
1139 if size_str == "0" {
1141 break;
1142 }
1143
1144 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 let mut crlf = [0u8; 2];
1152 r.read_exact(&mut crlf)?;
1153 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}
1221const 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}