http_windows.go (3249B)
1 package nativehttp 2 3 import ( 4 "io" 5 "runtime" 6 "unsafe" 7 8 . "git.sr.ht/~jackmordaunt/nativehttp/core" 9 "git.sr.ht/~jackmordaunt/nativehttp/core/windows" 10 "git.sr.ht/~jackmordaunt/nativehttp/winhttp" 11 ) 12 13 func get(s string) (io.ReadCloser, error) { 14 url, err := ParseURL(s) 15 if err != nil { 16 return nil, Err{Cause: err, Msg: "parsing url"} 17 } 18 19 domain, path, port := url.Domain, url.Path, url.Port 20 21 agentStr, err := windows.UTF16PtrFromString("HTTP nativehttp/1.0") 22 if err != nil { 23 return nil, err 24 } 25 26 session := winhttp.Open( 27 agentStr, 28 winhttp.AcessTypeDefaultProxy, 29 nil, 30 nil, 31 winhttp.OpenFlagAsync, 32 ) 33 if session == 0 { 34 return nil, windows.GetLastError() 35 } 36 37 defer Cleanup(&err, func() error { 38 if ok := winhttp.CloseHandle(session); ok == 0 { 39 return windows.GetLastError() 40 } 41 return nil 42 }) 43 44 domainStr, err := windows.UTF16PtrFromString(domain) 45 if err != nil { 46 return nil, err 47 } 48 49 connect := winhttp.Connect(session, domainStr, port, 0) 50 if connect == 0 { 51 return nil, windows.GetLastError() 52 } 53 54 defer Cleanup(&err, func() error { 55 if ok := winhttp.CloseHandle(connect); ok == 0 { 56 return windows.GetLastError() 57 } 58 return nil 59 }) 60 61 pathStr, err := windows.UTF16PtrFromString(path) 62 if err != nil { 63 return nil, err 64 } 65 66 getStr, err := windows.UTF16PtrFromString("GET") 67 if err != nil { 68 return nil, err 69 } 70 71 var openRequestFlags winhttp.OpenRequestFlag 72 if url.Encrypted { 73 openRequestFlags = winhttp.OpenRequestFlagSecure 74 } 75 76 request := winhttp.OpenRequest( 77 connect, 78 getStr, 79 pathStr, 80 nil, 81 nil, 82 nil, 83 openRequestFlags|winhttp.OpenRequestFlagNullCodepage, 84 ) 85 if request == 0 { 86 return nil, windows.GetLastError() 87 } 88 89 defer Cleanup(&err, func() error { 90 if ok := winhttp.CloseHandle(request); ok == 0 { 91 return windows.GetLastError() 92 } 93 return nil 94 }) 95 96 if ok := winhttp.SendRequest(request, nil, 0, nil, 0, 0, 0); ok == 0 { 97 return nil, windows.GetLastError() 98 } 99 100 if ok := winhttp.ReceiveResponse(request, nil); ok == 0 { 101 return nil, windows.GetLastError() 102 } 103 104 runtime.KeepAlive(agentStr) 105 runtime.KeepAlive(domainStr) 106 runtime.KeepAlive(pathStr) 107 runtime.KeepAlive(getStr) 108 109 return &winhttpRequest{ 110 session: session, 111 connect: connect, 112 request: request, 113 }, nil 114 } 115 116 // winhttpRequest adapts a request into an [io.ReadCloser]. 117 type winhttpRequest struct { 118 session winhttp.HINTERNET 119 connect winhttp.HINTERNET 120 request winhttp.HINTERNET 121 buf []byte 122 } 123 124 var _ io.ReadCloser = (*winhttpRequest)(nil) 125 126 func newRequest(h winhttp.HINTERNET) *winhttpRequest { 127 return &winhttpRequest{request: h} 128 } 129 130 func (r *winhttpRequest) Read(p []byte) (int, error) { 131 var size winhttp.DWORD 132 var n winhttp.DWORD 133 134 if ok := winhttp.QueryDataAvailable(r.request, &size); ok == 0 { 135 return 0, windows.GetLastError() 136 } 137 138 if size <= 0 { 139 return 0, io.EOF 140 } 141 142 if len(r.buf) < int(size+1) { 143 r.buf = make([]byte, size+1) 144 } 145 146 data := unsafe.SliceData(r.buf) 147 148 if ok := winhttp.ReadData(r.request, unsafe.Pointer(data), size, &n); ok == 0 { 149 return 0, windows.GetLastError() 150 } 151 152 nn := copy(p, r.buf[:n]) 153 154 runtime.KeepAlive(data) 155 156 return nn, nil 157 } 158 159 func (r *winhttpRequest) Close() error { 160 if ok := winhttp.CloseHandle(r.request); ok == 0 { 161 return windows.GetLastError() 162 } 163 return nil 164 }