Skip to content

Commit

Permalink
Add http proxy mode (ozkatz#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
ozkatz authored Apr 17, 2024
2 parents 6dd3b73 + ad46eaf commit 1514d64
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 2 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ Downloading and extracting a specific object from within a zip file:
cz cat s3://example-bucket/path/to/archive.zip images/cat.png > cat.png
```

HTTP proxy mode (see below):

```shell
cz http s3://example-bucket/path
```

Mounting (See below):

```shell
Expand Down Expand Up @@ -102,6 +108,17 @@ Reading a file from the remote zip involves another HTTP range request: once we

Because zip files store each file (whether compressed or not) independently, this is enough to uncompress and write the file to `stdout`.

#### ⚠️ Experimental: `cz http`

CloudZip can run in proxy mode, allowing you to read archived files directly HTTP client (usually a browser).

```shell
cz http s3://example-bucket/path
```

This will open an HTTP server on a random port (use `--listen` to bind to another address). The server will map the requested path relative to the supplied S3 url argument. A single query argument `filename` should be supplied, referencing the file within the zip file. E.g. `GET /a/b/c.zip?filename=foobar.png` will serve `foobar.png` from within the `s3://example-bucket/path/a/b/c.zip` archive.


#### ⚠️ Experimental: `cz mount`

Instead of listing and downloading individual files from the remote zip, you can now mount it to a local directory.
Expand Down
2 changes: 1 addition & 1 deletion cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func die(fstring string, args ...interface{}) {
func setupLogging() {
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelError,
Level: slog.LevelWarn,
})))
if os.Getenv("CLOUDZIP_LOGGING") == "DEBUG" {
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Expand Down
67 changes: 67 additions & 0 deletions cmd/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cmd

import (
"errors"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"strings"

"github.com/ozkatz/cloudzip/pkg/remote"
"github.com/ozkatz/cloudzip/pkg/zipfile"
"github.com/spf13/cobra"
)

var httpCmd = &cobra.Command{
Use: "http",
Short: "Run HTTP proxy server mode",
Example: "http s3://example-bucket/path",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
remotePath := strings.TrimSuffix(args[0], "/")
bindAddress, err := cmd.Flags().GetString("listen")
if err != nil {
die("Could not parse command flag listen: %v\n", err)
}

http.HandleFunc("/",
func(w http.ResponseWriter, r *http.Request) {
internalPath := r.URL.Query().Get("filename")
slog.Debug("HTTP Handler", "objectPath", r.URL.Path, "internalPath", internalPath)
obj, err := remote.Object(remotePath + r.URL.Path)
if err != nil {
slog.Warn("could not open zip file", "error", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
zip := zipfile.NewCentralDirectoryParser(zipfile.NewStorageAdapter(r.Context(), obj))
reader, err := zip.Read(internalPath)
if errors.Is(err, remote.ErrDoesNotExist) || errors.Is(err, zipfile.ErrFileNotFound) {
w.WriteHeader(http.StatusNotFound)
return
} else if err != nil {
w.WriteHeader(http.StatusBadGateway)
slog.Warn("Error reading zip file from upstream", "error", err)
return
}
io.Copy(w, reader)
})

listener, err := net.Listen("tcp", bindAddress)
if err != nil {
die("Failed to bind port: %v\n", err)
}
fmt.Printf("HTTP server listening on %s\n", listener.Addr().String())
err = http.Serve(listener, nil)
if err != nil {
slog.Error("Error running HTTP server", "error", err)
}
},
}

func init() {
httpCmd.Flags().StringP("listen", "l", "127.0.0.1:0", "address to listen on")
rootCmd.AddCommand(httpCmd)
}
3 changes: 2 additions & 1 deletion pkg/remote/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func s3IsNotFoundErr(err error) bool {
return false
}
var nf *types.NotFound
return errors.As(err, &nf)
var nosuchkey *types.NoSuchKey
return errors.As(err, &nf) || errors.As(err, &nosuchkey)
}

func s3parseUri(uri string) (*s3ParsedUri, error) {
Expand Down

0 comments on commit 1514d64

Please sign in to comment.