github.com/gospider007/requests@v0.0.0-20240506025355-c73d46169a23/tools.go (about) 1 package requests 2 3 import ( 4 "bufio" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "net/http" 11 "net/textproto" 12 "net/url" 13 "strconv" 14 "strings" 15 _ "unsafe" 16 17 "github.com/gospider007/ja3" 18 "golang.org/x/exp/slices" 19 "golang.org/x/net/http/httpguts" 20 ) 21 22 func getHost(req *http.Request) string { 23 host := req.Host 24 if host == "" { 25 host = req.URL.Host 26 } 27 _, port, _ := net.SplitHostPort(host) 28 if port == "" { 29 if req.URL.Scheme == "https" { 30 port = "443" 31 } else { 32 port = "80" 33 } 34 return fmt.Sprintf("%s:%s", host, port) 35 } 36 return host 37 } 38 func getAddr(uurl *url.URL) (addr string) { 39 if uurl == nil { 40 return "" 41 } 42 _, port, _ := net.SplitHostPort(uurl.Host) 43 if port == "" { 44 if uurl.Scheme == "https" { 45 port = "443" 46 } else { 47 port = "80" 48 } 49 return fmt.Sprintf("%s:%s", uurl.Host, port) 50 } 51 return uurl.Host 52 } 53 func cloneUrl(u *url.URL) *url.URL { 54 if u == nil { 55 return nil 56 } 57 r := *u 58 return &r 59 } 60 61 var replaceMap = map[string]string{ 62 "Sec-Ch-Ua": "sec-ch-ua", 63 "Sec-Ch-Ua-Mobile": "sec-ch-ua-mobile", 64 "Sec-Ch-Ua-Platform": "sec-ch-ua-platform", 65 } 66 67 //go:linkname escapeQuotes mime/multipart.escapeQuotes 68 func escapeQuotes(string) string 69 70 //go:linkname readCookies net/http.readCookies 71 func readCookies(h http.Header, filter string) []*http.Cookie 72 73 //go:linkname readSetCookies net/http.readSetCookies 74 func readSetCookies(h http.Header) []*http.Cookie 75 76 //go:linkname ReadRequest net/http.readRequest 77 func ReadRequest(b *bufio.Reader) (*http.Request, error) 78 79 //go:linkname removeZone net/http.removeZone 80 func removeZone(host string) string 81 82 //go:linkname shouldSendContentLength net/http.(*transferWriter).shouldSendContentLength 83 func shouldSendContentLength(t *http.Request) bool 84 85 //go:linkname removeEmptyPort net/http.removeEmptyPort 86 func removeEmptyPort(host string) string 87 88 //go:linkname redirectBehavior net/http.redirectBehavior 89 func redirectBehavior(reqMethod string, resp *http.Response, ireq *http.Request) (redirectMethod string, shouldRedirect, includeBody bool) 90 91 //go:linkname readTransfer net/http.readTransfer 92 func readTransfer(msg any, r *bufio.Reader) (err error) 93 94 var filterHeaderKeys = ja3.DefaultOrderHeadersWithH2() 95 96 func httpWrite(r *http.Request, w *bufio.Writer, orderHeaders []string) (err error) { 97 for i := range orderHeaders { 98 orderHeaders[i] = textproto.CanonicalMIMEHeaderKey(orderHeaders[i]) 99 } 100 host := r.Host 101 if host == "" { 102 host = r.URL.Host 103 } 104 host, err = httpguts.PunycodeHostPort(host) 105 if err != nil { 106 return err 107 } 108 host = removeZone(host) 109 ruri := r.URL.RequestURI() 110 if r.Method == "CONNECT" && r.URL.Path == "" { 111 if r.URL.Opaque != "" { 112 ruri = r.URL.Opaque 113 } else { 114 ruri = host 115 } 116 } 117 if r.Header.Get("Host") == "" { 118 r.Header.Set("Host", host) 119 } 120 if r.Header.Get("Connection") == "" { 121 r.Header.Set("Connection", "keep-alive") 122 } 123 if r.Header.Get("User-Agent") == "" { 124 r.Header.Set("User-Agent", UserAgent) 125 } 126 if r.Header.Get("Content-Length") == "" && r.ContentLength != 0 && shouldSendContentLength(r) { 127 r.Header.Set("Content-Length", fmt.Sprint(r.ContentLength)) 128 } 129 if _, err = w.WriteString(fmt.Sprintf("%s %s %s\r\n", r.Method, ruri, r.Proto)); err != nil { 130 return err 131 } 132 for _, k := range orderHeaders { 133 if vs, ok := r.Header[k]; ok { 134 if k2, ok := replaceMap[k]; ok { 135 k = k2 136 } 137 if slices.Contains(filterHeaderKeys, k) { 138 continue 139 } 140 for _, v := range vs { 141 if _, err = w.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)); err != nil { 142 return err 143 } 144 } 145 } 146 } 147 for k, vs := range r.Header { 148 if !slices.Contains(orderHeaders, k) { 149 if k2, ok := replaceMap[k]; ok { 150 k = k2 151 } 152 if slices.Contains(filterHeaderKeys, k) { 153 continue 154 } 155 for _, v := range vs { 156 if _, err = w.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)); err != nil { 157 return err 158 } 159 } 160 } 161 } 162 if _, err = w.WriteString("\r\n"); err != nil { 163 return err 164 } 165 if r.Body != nil { 166 if _, err = io.Copy(w, r.Body); err != nil { 167 return err 168 } 169 } 170 return w.Flush() 171 } 172 func NewRequestWithContext(ctx context.Context, method string, u *url.URL, body io.Reader) (*http.Request, error) { 173 req := (&http.Request{}).WithContext(ctx) 174 if method == "" { 175 req.Method = http.MethodGet 176 } else { 177 req.Method = strings.ToUpper(method) 178 } 179 req.URL = u 180 req.Proto = "HTTP/1.1" 181 req.ProtoMajor = 1 182 req.ProtoMinor = 1 183 req.Host = u.Host 184 u.Host = removeEmptyPort(u.Host) 185 if body != nil { 186 if v, ok := body.(interface{ Len() int }); ok { 187 req.ContentLength = int64(v.Len()) 188 } 189 rc, ok := body.(io.ReadCloser) 190 if !ok { 191 rc = io.NopCloser(body) 192 } 193 req.Body = rc 194 } 195 return req, nil 196 } 197 198 func readResponse(tp *textproto.Reader, req *http.Request) (*http.Response, error) { 199 resp := &http.Response{ 200 Request: req, 201 } 202 // Parse the first line of the response. 203 line, err := tp.ReadLine() 204 if err != nil { 205 if err == io.EOF { 206 err = io.ErrUnexpectedEOF 207 } 208 return nil, err 209 } 210 proto, status, ok := strings.Cut(line, " ") 211 if !ok { 212 return nil, errors.New("malformed HTTP response") 213 } 214 resp.Proto = proto 215 resp.Status = strings.TrimLeft(status, " ") 216 statusCode, _, _ := strings.Cut(resp.Status, " ") 217 if resp.StatusCode, err = strconv.Atoi(statusCode); err != nil { 218 return nil, errors.New("malformed HTTP status code") 219 } 220 if resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto); !ok { 221 return nil, errors.New("malformed HTTP version") 222 } 223 // Parse the response headers. 224 mimeHeader, err := tp.ReadMIMEHeader() 225 if err != nil { 226 if err == io.EOF { 227 err = io.ErrUnexpectedEOF 228 } 229 return nil, err 230 } 231 resp.Header = http.Header(mimeHeader) 232 return resp, readTransfer(resp, tp.R) 233 } 234 235 func addCookie(req *http.Request, cookies Cookies) { 236 cooks := Cookies(readCookies(req.Header, "")) 237 for _, cook := range cookies { 238 if val := cooks.Get(cook.Name); val == nil { 239 cooks = cooks.append(cook) 240 } 241 } 242 if result := cooks.String(); result != "" { 243 req.Header.Set("Cookie", result) 244 } 245 }