gostd_http/
client.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)]
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;
23// use tokio::io::AsyncBufRead;
24// use tokio::io::AsyncRead;
25
26/// DefaultMaxHeaderBytes is the maximum permitted size of the headers in an HTTP request. This can be overridden by setting Server.MaxHeaderBytes.
27
28const DefaultMaxHeaderBytes: int32 = 1 << 20;
29/// DefaultMaxIdleConnsPerHost is the default value of Transport's MaxIdleConnsPerHost.
30const DefaultMaxIdleConnsPerHost: int32 = 2;
31
32/// 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.
33///
34/// For parsing this time format, see ParseTime.
35const TimeFormat: &str = "Mon, 02 Jan 2006 15:04:05 GMT";
36
37/// 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.
38///
39/// 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:
40const TrailerPrefix: &str = "Trailer:";
41
42/// 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:
43/// ```text
44/// 301 (Moved Permanently)
45/// 302 (Found)
46/// 303 (See Other)
47/// 307 (Temporary Redirect)
48/// 308 (Permanent Redirect)
49/// ```
50/// Get is a wrapper around Client.Get.
51/// <details class="rustdoc-toggle top-doc">
52/// <summary class="docblock">zh-cn</summary>
53/// Get向指定的URL发出一个GET请求,如果回应的状态码如下,Get会在调用c.CheckRedirect后执行重定向
54///Get是对包变量Client的Get方法的包装。
55/// </details>
56///
57/// # Example
58///
59/// ```
60/// use gostd_http as http;
61/// // or use gostd::net::http;
62/// fn main() -> Result<(), Box<dyn std::error::Error>> {
63///     let url = "https://petstore.swagger.io/v2/pet/findByStatus?status=available";
64///     let response = http::Get(url)?;///
65///     println!(
66///         "{}",
67///         String::from_utf8(response.Body.expect("return body error").to_vec()).unwrap()
68///     );
69///     Ok(())
70/// }
71/// ```
72pub 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
80/// Post issues a POST to the specified URL. Post is a wrapper around DefaultClient.Post.
81/// To set custom headers, use Request::New and Client.Do.
82/// <details class="rustdoc-toggle top-doc">
83/// <summary class="docblock">zh-cn</summary>
84/// Post向指定的URL发出一个POST请求。bodyType为POST数据的类型, body为POST数据,作为请求的主体
85/// </details>
86///
87/// # Example
88///
89/// ```
90/// use gostd_http as http;
91/// // or use gostd::net::http;
92/// fn main() -> Result<(), Box<dyn std::error::Error>> {
93///     let url = "https://petstore.swagger.io/v2/pet";
94///     let postbody = r#"{"id":0,"category":{"id":0,"name":"string"},"name":"doggie","photoUrls":["string"],"tags":[{"id":0,"name":"string"}],"status":"available"}"#
95///    .as_bytes()
96///    .to_vec();
97///    let response = http::Post(url, "application/json", Some(postbody.into()))?;
98///
99///    println!(
100///        "{}",
101///        String::from_utf8(response.Body.expect("return body error").to_vec()).unwrap()
102///    );
103///
104///    Ok(())
105/// }
106///
107/// ```
108pub 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    // CheckRedirect: fn(req: &Request, via: Vec<&Request>) -> Result<(), Error>,
131    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    // idleMu: sync::Mutex,
291    closeIdle: bool,
292    // idleConn:HashMap<String, Vec<>>
293    Proxy: Option<url::URL>,
294    // Dial: fn(network: &str, addr: &str) -> Result<net::TcpConn, Error>,
295    ForceAttemptHTTP2: bool,
296    MaxIdleConns: int,
297    // IdleConnTimeout:       90 * time.Second,
298    // TLSHandshakeTimeout:   10 * time.Second,
299    // ExpectContinueTimeout: 1 * time.Second,
300    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        // conn.set_write_timeout(Some(std::time::Duration::new(5, 0)));
327        // conn.set_read_timeout(Some(std::time::Duration::new(5, 0)));
328
329        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        // pconn.t = self;
344        // pconn.reqch = mpsc::channel();
345        // pconn.writech = mpsc::channel();
346        // pconn.writeLoopDone = mpsc::channel();
347        self.dial("tcp", cm.addr().as_str())
348        // pconn.conn = conn;
349        // pconn.br = bufio::NewReaderSize(pconn, self.readBufferSize());
350        // pconn.bw = bufio::NewWriterSize(persistConnWriter { pconn }, self.writeBufferSize());
351        // 待实现读写进程
352        /* go pconn.readLoop()
353        go pconn.writeLoop() */
354        // Ok(pconn)
355    }
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; //待优化
367        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>, // nil for no proxy, else full proxy URL
420    targetScheme: String,       // "http" or "https"
421    // If proxyURL specifies an http or https proxy, and targetScheme is http (not https),
422    // then targetAddr is not included in the connect method key, because the socket can
423    // be reused for different targetAddr values.
424    targetAddr: String,
425    onlyH1: bool, // whether to disable HTTP/2 and force HTTP/1
426}
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    // br: bufio.Reader,
442    // bw: bufio.Writer,
443    nwrite: int64,
444    // reqch: channel,
445    // writech: channel,
446    isProxy: bool,
447    sawEOF: bool,
448    readLimit: int64,
449    // writeErrch: channel,
450    // writeLoopDone: channel,
451    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            // req.extra = Some(req.Req.Header.clone());
478            // let mut hd = req.extra.take().unwrap();
479            // // hd.Set("Accept-Encoding", "gzip");
480
481            // req.extra = Some(hd.clone());
482            // req.Req.Header = hd;
483        }
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    // parse status line。
542    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    // 1. 获取response的header部分,到第一个 '\r\b'独立行为header的结束。
564    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    // parse headPart
575    resp.Header = Header::NewWithHashMap(parseHeader(&headPart)?);
576    fixPragmaCacheControl(&mut resp.Header);
577
578    // set Body
579    if resp.Header.Get("Transfer-Encoding").as_str() == "chunked" {
580        // 2.chunked方式传输方式。获取body数据。
581        resp.Body = Some(parseChunkedBody(r)?);
582    } else {
583        // 3. 除chunked外的其他传输方式,都有Content-Length字段,根据长度获取body
584        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]; // 生成固定长度的数组,用于读取定长数据;
592        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
599// chunk数据是以16位数据长度 7acc\r\n独立行开头+ [data] 下一行以\r\n结尾数据段形式,所以数据的结尾用0\r\n表示。
600fn 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        // 校验开头行是\r\n结尾的chuank size行
605        if size_buf.ends_with(b"\r\n") {
606            // 删除尾部的\r\n,只保留表示大小的字符串
607            size_buf.truncate(size_buf.len() - 2); // Remove "\r\n"
608
609            // 16进制chunk大小字符串
610            let size_str = std::str::from_utf8(&size_buf)?;
611
612            // 如果字符串等于"0",已经到最后一个chunk数据段。
613            if size_str == "0" {
614                break;
615            }
616
617            // 按chunk长度读取分段的实际数据
618            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            //读取每个chunk data 结尾的\r\n,并丢弃掉
624            let mut crlf = [0u8; 2];
625            r.read_exact(&mut crlf)?;
626            //chuank size 行的数据要清空
627            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
680// Header KE规范化 content-length|CONTENT-LENGTH => Content-Length
681const 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}