1#![allow(unused)]
10#![allow(non_upper_case_globals)]
12#![allow(non_snake_case)]
13#![allow(non_camel_case_types)]
14use error::{HTTPConnectError, HttpResult};
15use gostd_builtin::*;
16use gostd_io::*;
17use gostd_strings as strings;
18use gostd_time as time;
19use gostd_url as url;
20use std::collections::HashMap;
21use std::collections::HashSet;
22use std::convert::TryInto;
23const DefaultMaxHeaderBytes: int32 = 1 << 20;
29const DefaultMaxIdleConnsPerHost: int32 = 2;
31
32const TimeFormat: &str = "Mon, 02 Jan 2006 15:04:05 GMT";
36
37const TrailerPrefix: &str = "Trailer:";
41
42pub fn Get(url: &str) -> HttpResult<Response> {
73 Client::New().Get(url)
74}
75
76pub fn Head(url: &str) -> HttpResult<Response> {
77 Client::New().Head(url)
78}
79
80pub fn Post(url: &str, contentType: &str, body: Option<Bytes>) -> HttpResult<Response> {
109 Client::New().Post(url, contentType, body)
110}
111
112pub fn PostForm(url: &str, data: url::Values) -> HttpResult<Response> {
113 Client::New().PostForm(url, data)
114}
115
116pub fn Patch(url: &str, body: Option<Bytes>) -> HttpResult<Response> {
117 Client::New().Patch(url, body)
118}
119
120pub fn Put(url: &str, body: Option<Bytes>) -> HttpResult<Response> {
121 Client::New().Put(url, body)
122}
123
124pub fn Delete(url: &str) -> HttpResult<Response> {
125 Client::New().Delete(url)
126}
127
128pub struct Client {
129 Transport: Box<dyn RoundTripper>,
130 Jar: Box<dyn CookieJar>,
132 Timeout: time::Duration,
133}
134impl Default for Client {
135 fn default() -> Self {
136 Self {
137 Transport: Box::new(Transport::default()),
138 Timeout: time::Duration::new(0),
139 Jar: Box::new(Cookie::default()),
140 }
141 }
142}
143
144impl Client {
145 pub fn New() -> Self {
146 Self::default()
147 }
148
149 pub fn Get(&mut self, url: &str) -> HttpResult<Response> {
150 let mut req = Request::New(Method::Get, url, None)?;
151 self.Do(&mut req)
152 }
153
154 pub fn Post(
155 &mut self,
156 url: &str,
157 contentType: &str,
158 body: Option<Bytes>,
159 ) -> HttpResult<Response> {
160 let mut req = Request::New(Method::Post, url, body)?;
161 req.Header.Set("Content-Type", contentType);
162 self.Do(&mut req)
163 }
164
165 pub fn PostForm(&mut self, url: &str, data: url::Values) -> HttpResult<Response> {
166 self.Post(
167 url,
168 "application/x-www-form-urlencoded",
169 Some(data.Encode().into_bytes().into()),
170 )
171 }
172
173 pub fn Head(&mut self, url: &str) -> HttpResult<Response> {
174 let mut req = Request::New(Method::Head, url, None)?;
175 self.Do(&mut req)
176 }
177
178 pub fn Patch(&mut self, url: &str, body: Option<Bytes>) -> HttpResult<Response> {
179 let mut req = Request::New(Method::Patch, url, body)?;
180 self.Do(&mut req)
181 }
182
183 pub fn Put(&mut self, url: &str, body: Option<Bytes>) -> HttpResult<Response> {
184 let mut req = Request::New(Method::Put, url, body)?;
185 self.Do(&mut req)
186 }
187
188 pub fn Delete(&mut self, url: &str) -> HttpResult<Response> {
189 let mut req = Request::New(Method::Delete, url, None)?;
190 self.Do(&mut req)
191 }
192
193 pub fn Do(&mut self, req: &mut Request) -> HttpResult<Response> {
194 self.done(req)
195 }
196
197 fn send(
198 &mut self,
199 req: &mut Request,
200 deadline: time::Time,
201 ) -> HttpResult<(Response, fn() -> bool)> {
202 let (resp, didTimeout) = send(req, self.transport(), deadline)?;
203 Ok((resp, didTimeout))
204 }
205
206 fn done(&mut self, req: &mut Request) -> HttpResult<Response> {
207 let deadline = self.deadline();
208 let (resp, didTimeout) = self.send(req, deadline)?;
209 Ok(resp)
210 }
211
212 fn deadline(&mut self) -> time::Time {
213 if self.Timeout > time::Duration::new(0) {
214 return time::Now().Add(&self.Timeout);
215 }
216 time::Time::default()
217 }
218
219 fn transport(&self) -> Box<dyn RoundTripper> {
220 Box::new(Transport::default())
221 }
222}
223
224fn send(
225 ireq: &mut Request,
226 mut rt: Box<dyn RoundTripper>,
227 deadline: time::Time,
228) -> HttpResult<(Response, fn() -> bool)> {
229 let mut resp = Response::default();
230 fn didTimeout() -> bool {
231 return false;
232 };
233 loop {
234 let mut resp = rt.RoundTrip(ireq)?;
235 let mut loc = resp.Header.Get("Location");
236 let (redirectMethod, shouldRedirect, includeBody) =
237 redirectBehavior(ireq.Method.as_str(), &resp, ireq);
238 if !shouldRedirect {
239 return Ok((resp, didTimeout));
240 }
241 let mut u = ireq.URL.Parse(loc.as_str())?;
242 let urlRef = refererForURL(&ireq.URL, &u);
243 ireq.Method = redirectMethod.clone();
244 ireq.URL = u.clone();
245 ireq.Header.Set("Referer", urlRef.as_str());
246 }
247}
248
249pub fn redirectBehavior(reqMethod: &str, resp: &Response, ireq: &Request) -> (String, bool, bool) {
250 let mut shouldRedirect = false;
251 let mut includeBody = false;
252 match resp.StatusCode {
253 301 | 302 | 303 => return (Method::Get.String().to_string(), true, false),
254 307 | 308 => {
255 if resp.Header.Get("Location") == "" {
256 shouldRedirect = true;
257 includeBody = false;
258 }
259 if ireq.Body.is_none() && ireq.ContentLength != 0 {
260 shouldRedirect = false;
261 }
262 }
263 _ => (),
264 }
265 (reqMethod.to_string(), shouldRedirect, includeBody)
266}
267
268pub trait RoundTripper {
269 fn RoundTrip(&mut self, r: &Request) -> HttpResult<Response>;
270}
271
272pub fn refererForURL(lastReq: &url::URL, newReq: &url::URL) -> String {
273 if (lastReq.Scheme == "https") && (newReq.Scheme == "http") {
274 return "".to_string();
275 }
276 let mut referer = lastReq.String();
277 if let Some(user) = lastReq.User.clone() {
278 return referer;
279 }
280 let auth = "@";
281 referer = strings::Replace(referer.as_str(), auth, "", 1);
282 referer
283}
284
285use std::io;
286use std::iter::FromIterator;
287use std::sync;
288#[derive(Default, Clone)]
289pub struct Transport {
290 closeIdle: bool,
292 Proxy: Option<url::URL>,
294 ForceAttemptHTTP2: bool,
296 MaxIdleConns: int,
297 DisableKeepAlives: bool,
301
302 DisableCompression: bool,
303 iMaxIdleConnsPerHost: int,
304 MaxConnsPerHost: int,
305 MaxResponseHeaderBytes: int64,
306 WriteBufferSize: int,
307 ReadBufferSize: int,
308 tlsNextProtoWasNil: bool,
309}
310
311use std::net;
312use std::sync::mpsc;
313impl RoundTripper for Transport {
314 fn RoundTrip(&mut self, req: &Request) -> HttpResult<Response> {
315 self.roundTrip(req)
316 }
317}
318impl Transport {
319 fn roundTrip(&mut self, req: &Request) -> HttpResult<Response> {
320 let treq = &mut transportRequest {
321 Req: req.clone(),
322 extra: None,
323 };
324 let cm = self.connectMethodForRequest(treq)?;
325 let (mut pconn, mut conn) = self.getConn(treq, cm)?;
326 pconn.roundTrip(treq, conn)
330 }
331
332 fn getConn(
333 &mut self,
334 treq: &transportRequest,
335 cm: connectMethod,
336 ) -> HttpResult<(persistConn, TcpConn)> {
337 let conn = self.dialConn(cm)?;
338 let pconn = persistConn::default();
339 Ok((pconn, conn))
340 }
341
342 fn dialConn(&mut self, cm: connectMethod) -> HttpResult<TcpConn> {
343 self.dial("tcp", cm.addr().as_str())
348 }
356
357 fn dial(&mut self, network: &str, addr: &str) -> HttpResult<TcpConn> {
358 Ok(net::TcpStream::connect(addr)?)
359 }
360
361 fn connectMethodForRequest(&mut self, treq: &transportRequest) -> HttpResult<connectMethod> {
362 let mut cm = connectMethod::default();
363 cm.targetScheme = treq.Req.URL.Scheme.clone();
364 cm.targetAddr = canonicalAddr(&treq.Req.URL.clone());
365 cm.proxyURL = None;
366 cm.onlyH1 = true; Ok(cm)
368 }
369
370 fn wirteBufferSize(self) -> int {
371 if self.WriteBufferSize > 0 {
372 return self.WriteBufferSize;
373 }
374 4 << 10
375 }
376
377 fn readBufferSize(self) -> int {
378 if self.ReadBufferSize > 0 {
379 return self.ReadBufferSize;
380 }
381 4 << 10
382 }
383}
384
385fn canonicalAddr(url: &url::URL) -> String {
386 let portMap: HashMap<String, String> = [
387 ("http".to_string(), "80".to_string()),
388 ("https".to_string(), "443".to_string()),
389 ("socks5".to_string(), "1080".to_string()),
390 ]
391 .iter()
392 .cloned()
393 .collect();
394 let addr = url.Hostname().to_string();
395 let mut port = url.Port().to_string();
396 if port == "" {
397 port = portMap.get(url.Scheme.as_str()).unwrap().to_string();
398 }
399 strings::Join(vec![addr.as_str(), port.as_str()], ":")
400}
401
402#[derive(Default, Clone)]
403struct transportRequest {
404 pub Req: Request,
405 extra: Option<Header>,
406}
407
408impl transportRequest {
409 fn extraHeaders(&mut self) -> Header {
410 if let Some(extra) = self.extra.clone() {
411 return extra;
412 }
413 Header::default()
414 }
415}
416
417#[derive(Default, PartialEq, PartialOrd, Clone)]
418struct connectMethod {
419 proxyURL: Option<url::URL>, targetScheme: String, targetAddr: String,
425 onlyH1: bool, }
427impl connectMethod {
428 fn scheme(&self) -> String {
429 self.targetScheme.clone()
430 }
431
432 pub fn addr(&self) -> String {
433 self.targetAddr.clone()
434 }
435}
436type TcpConn = TcpStream;
437use std::sync::mpsc::channel;
438#[derive(Default, Clone)]
439struct persistConn {
440 t: Transport,
441 nwrite: int64,
444 isProxy: bool,
447 sawEOF: bool,
448 readLimit: int64,
449 numExpectedResponses: int,
452 broken: bool,
453 reused: bool,
454}
455
456use bytes::Bytes;
457use rustls::ClientConnection;
458use rustls::StreamOwned;
459use std::convert::TryFrom;
460use std::io::prelude::*;
461use std::io::BufReader;
462use std::net::Shutdown;
463use std::net::TcpStream;
464use std::rc::Rc;
465use std::sync::Arc;
466use webpki_roots::TLS_SERVER_ROOTS;
467impl persistConn {
468 fn roundTrip(&mut self, req: &mut transportRequest, mut conn: TcpConn) -> HttpResult<Response> {
469 self.numExpectedResponses += 1;
470 let mut requestedGzip = false;
471 if !self.t.DisableCompression
472 && req.Req.Header.Get("Accept-Encoding") == ""
473 && req.Req.Header.Get("Range") == ""
474 && req.Req.Method != "HEAD".to_string()
475 {
476 requestedGzip = true;
477 }
484 if req.Req.Close {
485 req.Req.Header.Set("Connection", "close");
486 }
487
488 let r = req.Req.Write()?;
489
490 if req.Req.isTLS {
491 let mut tlsConn = getTLSConn(req.Req.Host.as_str(), conn)?;
492 tlsConn.write(r.as_slice())?;
493 let mut reader = BufReader::new(tlsConn);
494 let resp = ReadResponse(reader, &req.Req)?;
495 Ok(resp)
496 } else {
497 conn.write(r.as_slice())?;
498 let mut reader = BufReader::new(conn);
499 let resp = ReadResponse(reader, &req.Req)?;
500 Ok(resp)
501 }
502 }
503}
504use bytes::{Buf, BytesMut};
505use rustls::pki_types::ServerName;
506use rustls::{ClientConfig, RootCertStore};
507use std::io::ErrorKind;
508
509use crate::cookies::Cookie;
510use crate::cookies::CookieJar;
511use crate::error;
512use crate::header::Header;
513use crate::method::Method;
514use crate::request::Request;
515use crate::response::Response;
516
517fn get_tls_config() -> Arc<ClientConfig> {
518 let mut clientRootCert = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned());
519
520 Arc::new(
521 ClientConfig::builder()
522 .with_root_certificates(clientRootCert)
523 .with_no_client_auth(),
524 )
525}
526
527fn getTLSConn(
528 dnsName: &str,
529 socket: TcpConn,
530) -> HttpResult<StreamOwned<ClientConnection, TcpConn>> {
531 let tlsconfig = get_tls_config();
532 let serverName = ServerName::try_from(dnsName.to_owned())?;
533 let mut tlsClient = ClientConnection::new(tlsconfig, serverName)?;
534 let mut tlsConn = StreamOwned::new(tlsClient, socket);
535 Ok(tlsConn)
536}
537
538pub fn ReadResponse(mut r: impl BufRead, req: &Request) -> HttpResult<Response> {
539 let mut resp = Response::default();
540 resp.Request = req.clone();
541 let mut line = String::new();
543 r.read_line(&mut line)?;
544 let parts: Vec<&str> = line.split_whitespace().collect();
545 if parts.len() < 3 {
546 return Err(HTTPConnectError::ConnectionFailure(
547 "malformed HTTP response".to_string(),
548 ));
549 }
550 resp.Proto = parts[0].to_string();
551 resp.Status = parts[1..].join(" ");
552 resp.StatusCode = parts[1].parse::<isize>()?;
553 let vers = ParseHTTPVersion(&resp.Proto);
554 let ok = vers.2;
555 if !ok {
556 return Err(HTTPConnectError::ConnectionFailure(
557 "malformed HTTP version".to_string(),
558 ));
559 }
560 resp.ProtoMajor = vers.0;
561 resp.ProtoMinor = vers.1;
562
563 let mut headPart = BytesMut::new();
565 let mut head_line = String::new();
566 while r.read_line(&mut head_line).is_ok() {
567 if head_line.as_bytes() == b"\r\n" {
568 break;
569 }
570 headPart.extend_from_slice(head_line.as_bytes());
571 head_line.clear()
572 }
573
574 resp.Header = Header::NewWithHashMap(parseHeader(&headPart)?);
576 fixPragmaCacheControl(&mut resp.Header);
577
578 if resp.Header.Get("Transfer-Encoding").as_str() == "chunked" {
580 resp.Body = Some(parseChunkedBody(r)?);
582 } else {
583 let ln: usize = resp
585 .Header
586 .Get("Content-Length")
587 .as_str()
588 .parse::<usize>()
589 .expect("Content-Length is not exist");
590
591 let mut buf = vec![0; ln]; r.read_exact(&mut buf)?;
593 resp.Body = Some(BytesMut::from(&buf[..]));
594 }
595 resp.ContentLength = resp.Body.as_ref().map_or(0, |b| b.len() as i64);
596 Ok(resp)
597}
598
599fn parseChunkedBody(mut r: impl BufRead) -> HttpResult<BytesMut> {
601 let mut body = BytesMut::new();
602 let mut size_buf = vec![];
603 while r.read_until(b'\n', &mut size_buf).is_ok() {
604 if size_buf.ends_with(b"\r\n") {
606 size_buf.truncate(size_buf.len() - 2); let size_str = std::str::from_utf8(&size_buf)?;
611
612 if size_str == "0" {
614 break;
615 }
616
617 let chunk_size = usize::from_str_radix(size_str, 16)?;
619
620 let mut chunk_data = vec![0u8; chunk_size];
621 r.read_exact(&mut chunk_data)?;
622 body.extend_from_slice(&chunk_data);
623 let mut crlf = [0u8; 2];
625 r.read_exact(&mut crlf)?;
626 size_buf.clear();
628 }
629 }
630 Ok(body)
631}
632
633pub type MIMEHeader = HashMap<String, Vec<String>>;
634
635fn fixPragmaCacheControl(header: &mut Header) {
636 if let Some(hp) = header.0.get("Pragma") {
637 if len!(hp) > 0 && &hp[0] == "no-cache" && header.0.get("Cache-Control").is_none() {
638 header.Set("Cache-Control", "no-cache");
639 }
640 }
641}
642
643fn parseHeader(headPart: &[u8]) -> HttpResult<MIMEHeader> {
644 let mut m: MIMEHeader = HashMap::new();
645 let lines = std::str::from_utf8(headPart)?;
646
647 for kv in lines.split("\r\n") {
648 if let Some((key, value)) = kv.split_once(':') {
649 let key = canonicalMIMEHeaderKey(key);
650 if key.is_empty() {
651 continue;
652 }
653
654 let value = value
655 .trim_start_matches(|c: char| c == ' ' || c == '\t')
656 .trim_matches('"')
657 .to_string();
658
659 m.entry(key).or_insert_with(Vec::new).push(value);
660 }
661 }
662 Ok(m)
663}
664
665fn startIndexOfBody(response: &Vec<u8>) -> Option<usize> {
666 let mut sep: Vec<u8> = vec![];
667 for (i, b) in response.iter().map(|&x| x).enumerate() {
668 if b == b'\r' || b == b'\n' {
669 sep.push(b);
670 } else {
671 sep.clear();
672 }
673 if sep.as_slice() == b"\r\n\r\n" {
674 return Some(i);
675 }
676 }
677 None
678}
679
680const toLower: byte = (b'a' - b'A');
682fn canonicalMIMEHeaderKey(s: &str) -> String {
683 let mut upper = true;
684 let mut new = String::with_capacity(s.len());
685 for &byte in s.as_bytes() {
686 let c = if upper && byte >= b'a' && byte <= b'z' {
687 byte - toLower
688 } else if !upper && byte >= b'A' && byte <= b'Z' {
689 byte + toLower
690 } else {
691 byte
692 };
693 upper = c == b'-';
694 new.push(c as char);
695 }
696 new
697}
698
699pub fn ParseHTTPVersion(vers: &str) -> (int, int, bool) {
700 let big: int = 1_000_000;
701
702 if !vers.starts_with("HTTP/") {
703 return (0, 0, false);
704 }
705
706 let version_part = &vers[5..];
707 let parts: Vec<&str> = version_part.split('.').collect();
708
709 if parts.len() != 2 {
710 return (0, 0, false);
711 }
712
713 match (parts[0].parse::<int>(), parts[1].parse::<int>()) {
714 (Ok(major), Ok(minor)) if major >= 0 && major <= big && minor >= 0 && minor <= big => {
715 (major, minor, true)
716 }
717 _ => (0, 0, false),
718 }
719}