nativehttp

Simple interface to the native http client
Log | Files | Refs | README | LICENSE

commit 0205331de977c5f3970327c43078bc69877eec7b
parent 777550d534f2443cd95ecc3d2423ea7563d80248
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Tue, 13 Feb 2024 10:25:25 +0800

http: refactor in terms of the core package

Use all core logic from the core package.

Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>

Diffstat:
Mhttp_test.go | 3+--
Mhttp_windows.go | 224+++++++++++--------------------------------------------------------------------
2 files changed, 30 insertions(+), 197 deletions(-)

diff --git a/http_test.go b/http_test.go @@ -3,7 +3,6 @@ package nativehttp import ( "io" "net/http" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -14,7 +13,7 @@ func TestGet(t *testing.T) { Addr: "localhost:1337", Handler: http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) { t.Log("request") - if _, err := io.Copy(wr, strings.NewReader("hellope!")); err != nil { + if _, err := wr.Write([]byte("hellope!")); err != nil { t.Fatalf("failed copying data to response writer") } }), diff --git a/http_windows.go b/http_windows.go @@ -2,40 +2,23 @@ package nativehttp import ( "io" - "net/url" "runtime" - "syscall" "unsafe" + . "git.sr.ht/~jackmordaunt/nativehttp/core" + "git.sr.ht/~jackmordaunt/nativehttp/core/windows" "git.sr.ht/~jackmordaunt/nativehttp/winhttp" ) -func get(uri string) (io.ReadCloser, error) { - openRequestFlags := winhttp.OpenRequestFlagNone - port := uint32(winhttp.DEFAULT_PORT) - - if HasPrefix(uri, "https://") { - openRequestFlags = winhttp.OpenRequestFlagSecure - port = winhttp.DEFAULT_HTTPS_PORT - } - - u, err := url.Parse(uri) +func get(s string) (io.ReadCloser, error) { + url, err := ParseURL(s) if err != nil { - return nil, err + return nil, Err{Cause: err, Msg: "parsing url"} } - domain := u.Hostname() - path := TrimPrefix(u.Path, "/") + domain, path, port := url.Domain, url.Path, url.Port - if customPort := u.Port(); customPort != "" { - n, err := parseUint(customPort) - if err != nil { - return nil, err - } - port = uint32(n) - } - - agentStr, err := UTF16PtrFromString("HTTP Plato Desktop Spark/1.0") + agentStr, err := windows.UTF16PtrFromString("HTTP nativehttp/1.0") if err != nil { return nil, err } @@ -48,43 +31,48 @@ func get(uri string) (io.ReadCloser, error) { winhttp.OpenFlagAsync, ) if session == 0 { - return nil, GetLastError() + return nil, windows.GetLastError() } - defer cleanup(&err, func() error { + defer Cleanup(&err, func() error { if ok := winhttp.CloseHandle(session); ok == 0 { - return GetLastError() + return windows.GetLastError() } return nil }) - domainStr, err := UTF16PtrFromString(domain) + domainStr, err := windows.UTF16PtrFromString(domain) if err != nil { return nil, err } connect := winhttp.Connect(session, domainStr, port, 0) if connect == 0 { - return nil, GetLastError() + return nil, windows.GetLastError() } - defer cleanup(&err, func() error { + defer Cleanup(&err, func() error { if ok := winhttp.CloseHandle(connect); ok == 0 { - return GetLastError() + return windows.GetLastError() } return nil }) - pathStr, err := UTF16PtrFromString(path) + pathStr, err := windows.UTF16PtrFromString(path) if err != nil { return nil, err } - getStr, err := UTF16PtrFromString("GET") + getStr, err := windows.UTF16PtrFromString("GET") if err != nil { return nil, err } + var openRequestFlags winhttp.OpenRequestFlag + if url.Encrypted { + openRequestFlags = winhttp.OpenRequestFlagSecure + } + request := winhttp.OpenRequest( connect, getStr, @@ -95,22 +83,22 @@ func get(uri string) (io.ReadCloser, error) { openRequestFlags|winhttp.OpenRequestFlagNullCodepage, ) if request == 0 { - return nil, GetLastError() + return nil, windows.GetLastError() } - defer cleanup(&err, func() error { + defer Cleanup(&err, func() error { if ok := winhttp.CloseHandle(request); ok == 0 { - return GetLastError() + return windows.GetLastError() } return nil }) if ok := winhttp.SendRequest(request, nil, 0, nil, 0, 0, 0); ok == 0 { - return nil, GetLastError() + return nil, windows.GetLastError() } if ok := winhttp.ReceiveResponse(request, nil); ok == 0 { - return nil, GetLastError() + return nil, windows.GetLastError() } runtime.KeepAlive(agentStr) @@ -144,7 +132,7 @@ func (r *winhttpRequest) Read(p []byte) (int, error) { var n winhttp.DWORD if ok := winhttp.QueryDataAvailable(r.request, &size); ok == 0 { - return 0, GetLastError() + return 0, windows.GetLastError() } if size <= 0 { @@ -158,7 +146,7 @@ func (r *winhttpRequest) Read(p []byte) (int, error) { data := unsafe.SliceData(r.buf) if ok := winhttp.ReadData(r.request, unsafe.Pointer(data), size, &n); ok == 0 { - return 0, GetLastError() + return 0, windows.GetLastError() } nn := copy(p, r.buf[:n]) @@ -170,161 +158,7 @@ func (r *winhttpRequest) Read(p []byte) (int, error) { func (r *winhttpRequest) Close() error { if ok := winhttp.CloseHandle(r.request); ok == 0 { - return GetLastError() + return windows.GetLastError() } return nil } - -// cleanup executes a fallible function if the captured error is not nil. -func cleanup(err *error, fn func() error) { - if err != nil && *err != nil { - if e := fn(); e != nil { - *err = ErrorJoin(*err, e) - } - } -} - -/* - package strconv -*/ - -// parseUint is a specialized helper that parses a sequence of digits into an unsigned integer. -func parseUint(s string) (n uint, _ error) { - const asciiDigitOffset = 48 - - width := uint(len(s)) - - for ii := width; ii > 0; ii-- { - c := s[ii] - switch c { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - d := uint(c) - asciiDigitOffset - n += d * pow(10, width-ii) - default: - return 0, parseErr{literal: c} - } - } - - return n, nil -} - -type parseErr struct { - literal byte -} - -func (p parseErr) Error() string { - return "parse: invalid literal (not a digit) " + string(p.literal) -} - -/* - package math -*/ - -func pow(base, exponent uint) (n uint) { - n = 1 - for ii := uint(0); ii < exponent; ii++ { - n *= base - } - return n -} - -/* - package strings -*/ - -// TrimPrefix returns s without the provided leading prefix string. -// If s doesn't start with prefix, s is returned unchanged. -func TrimPrefix(s, prefix string) string { - if HasPrefix(s, prefix) { - return s[len(prefix):] - } - return s -} - -// HasPrefix tests whether the string s begins with prefix. -func HasPrefix(s, prefix string) bool { - return len(s) >= len(prefix) && s[0:len(prefix)] == prefix -} - -/* - package errors -*/ - -// ErrorJoin returns an error that wraps the given errors. -// Any nil error values are discarded. -// ErrorJoin returns nil if every value in errs is nil. -// The error formats as the concatenation of the strings obtained -// by calling the Error method of each element of errs, with a newline -// between each string. -// -// A non-nil error returned by ErrorJoin implements the Unwrap() []error method. -func ErrorJoin(errs ...error) error { - n := 0 - for _, err := range errs { - if err != nil { - n++ - } - } - if n == 0 { - return nil - } - e := &joinError{ - errs: make([]error, 0, n), - } - for _, err := range errs { - if err != nil { - e.errs = append(e.errs, err) - } - } - return e -} - -type joinError struct { - errs []error -} - -func (e *joinError) Error() string { - var b []byte - for i, err := range e.errs { - if i > 0 { - b = append(b, '\n') - } - b = append(b, err.Error()...) - } - return string(b) -} - -func (e *joinError) Unwrap() []error { - return e.errs -} - -// UTF16PtrFromString returns pointer to the UTF-16 encoding of -// the UTF-8 string s, with a terminating NUL added. If s -// contains a NUL byte at any location, it returns (nil, syscall.EINVAL). -func UTF16PtrFromString(s string) (*uint16, error) { - a, err := UTF16FromString(s) - if err != nil { - return nil, err - } - return &a[0], nil -} - -// UTF16FromString returns the UTF-16 encoding of the UTF-8 string -// s, with a terminating NUL added. If s contains a NUL byte at any -// location, it returns (nil, syscall.EINVAL). -func UTF16FromString(s string) ([]uint16, error) { - return syscall.UTF16FromString(s) -} - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32") - procGetLastError = modkernel32.NewProc("GetLastError") -) - -func GetLastError() (lasterr error) { - r0, _, _ := procGetLastError.Call() - if r0 != 0 { - lasterr = syscall.Errno(r0) - } - return -}