// Copyright 2024 Matthew Rich . All rights reserved. package transport import ( _ "errors" "io" _ "os" "net/url" "net/http" "strings" "fmt" "context" "path/filepath" "log/slog" "io/fs" ) type Pipe struct { Reader io.ReadCloser Writer io.WriteCloser } type HTTPConnection struct { stream *Pipe request *http.Request response *http.Response Client *http.Client } type HTTP struct { uri *url.URL path string gzip bool exttype string fileext string ctx context.Context get *HTTPConnection post *HTTPConnection Client *http.Client } func HTTPExists(u *url.URL) bool { return false } func NewPipe() *Pipe { r,w := io.Pipe() return &Pipe{ Reader: r, Writer: w } } func NewHTTPConnection(client *http.Client) *HTTPConnection { return &HTTPConnection { Client: client, } } func (h *HTTPConnection) NewPostRequest(ctx context.Context, uri string) (err error) { h.stream = NewPipe() h.request, err = http.NewRequestWithContext(ctx, "POST", uri, h.Reader()) return } func (h *HTTPConnection) NewGetRequest(ctx context.Context, uri string) (err error) { h.request, err = http.NewRequestWithContext(ctx, "GET", uri, nil) return } func (h *HTTPConnection) Request() *http.Request { return h.request } func (h *HTTPConnection) Response() *http.Response { return h.response } func (h *HTTPConnection) Writer() io.WriteCloser { return h.stream.Writer } func (h *HTTPConnection) Reader() io.ReadCloser { return h.stream.Reader } func (h *HTTPConnection) Do() (err error) { slog.Info("transport.HTTPConnection.Do()", "connection", h, "request", h.request) h.response, err = h.Client.Do(h.request) return } func (h *HTTPConnection) Read(p []byte) (n int, err error) { if h.response == nil { if err = h.Do(); err != nil { return } } return h.response.Body.Read(p) } func (h *HTTPConnection) Write(p []byte) (n int, err error) { if h.response == nil { if err = h.Do(); err != nil { return } } slog.Info("transport.HTTPConnection.Write()", "data", p, "connection", h) return h.Writer().Write(p) } func (h *HTTPConnection) ReadFrom(r io.Reader) (n int64, err error) { h.request.Body = r.(io.ReadCloser) if h.response == nil { if err = h.Do(); err != nil { return } } return h.request.ContentLength, nil } func (h *HTTPConnection) Close() (err error) { if h.response != nil { defer h.response.Body.Close() } if h.stream != nil { err = h.Writer().Close() } return } func NewHTTP(u *url.URL, ctx context.Context) (h *HTTP, err error) { h = &HTTP { ctx: ctx, uri: u, path: filepath.Join(u.Hostname(), u.RequestURI()), Client: http.DefaultClient, } h.extension() h.DetectGzip() return } func (h *HTTP) extension() { elements := strings.Split(h.path, ".") numberOfElements := len(elements) if numberOfElements > 2 { h.exttype = elements[numberOfElements - 2] h.fileext = elements[numberOfElements - 1] } h.exttype = elements[numberOfElements - 1] } func (h *HTTP) DetectGzip() { h.gzip = (h.uri.Query().Get("gzip") == "true" || h.fileext == "gz") } func (h *HTTP) URI() *url.URL { return h.uri } func (h *HTTP) Path() string { return h.path } func (h *HTTP) Signature() (documentSignature string) { if h.get.Response() != nil { documentSignature = h.get.Response().Header.Get("Signature") if documentSignature == "" { signatureResp, signatureErr := h.Client.Get(fmt.Sprintf("%s.sig", h.uri.String())) if signatureErr == nil { defer signatureResp.Body.Close() readSignatureBody, readSignatureErr := io.ReadAll(signatureResp.Body) if readSignatureErr == nil { documentSignature = string(readSignatureBody) } } } } return documentSignature } func (h *HTTP) Stat() (info fs.FileInfo, err error) { return } func (h *HTTP) ContentType() (contenttype string) { contenttype = h.get.Response().Header.Get("Content-Type") switch contenttype { case "application/octet-stream": return h.exttype default: } return } func (h *HTTP) SetGzip(gzip bool) { h.gzip = gzip } func (h *HTTP) Gzip() bool { return h.gzip } func (h *HTTP) Reader() io.ReadCloser { if h.get == nil { h.get = NewHTTPConnection(h.Client) if err := h.get.NewGetRequest(h.ctx, h.uri.String()); err != nil { panic(err) } } return h.get } func (h *HTTP) Writer() io.WriteCloser { if h.post == nil { h.post = NewHTTPConnection(h.Client) if err := h.post.NewPostRequest(h.ctx, h.uri.String()); err != nil { panic(err) } } return h.post } func (h *HTTP) ReadWriter() io.ReadWriteCloser { return nil } func (h *HTTP) GetRequest() *http.Request { return h.get.Request() } func (h *HTTP) GetResponse() *http.Response { return h.get.Response() } func (h *HTTP) PostRequest() *http.Request { return h.post.Request() } func (h *HTTP) PostResponse() *http.Response { return h.post.Response() }