diff options
Diffstat (limited to 'main.go')
-rw-r--r-- | main.go | 91 |
1 files changed, 88 insertions, 3 deletions
@@ -1,17 +1,20 @@ package main import ( + "bytes" "flag" "fmt" "io/ioutil" "log" "net" "net/http" + "net/url" "os" "strings" "time" "github.com/nats-io/nats.go" + "github.com/nats-io/nats.go/micro" ) // printHelp outputs the usage information. @@ -29,6 +32,46 @@ func printHelp() { fmt.Println(" HTTP_PORT - HTTP port to listen on (default: 8080)") } +// URL to NATS subject conversion +func URLToNATS(urlPath string) (string, error) { + segments := strings.Split(strings.Trim(urlPath, "/"), "/") + for i, seg := range segments { + // Decode existing encoding first to prevent double-encoding + unescaped, err := url.PathUnescape(seg) + if err != nil { + return "", fmt.Errorf("failed to unescape segment: %w", err) + } + + // Encode special NATS-sensitive characters + encoded := url.PathEscape(unescaped) + encoded = strings.ReplaceAll(encoded, ".", "%2E") // Critical for token separation + encoded = strings.ReplaceAll(encoded, "*", "%2A") // Wildcard protection + encoded = strings.ReplaceAll(encoded, ">", "%3E") // Wildcard protection + + segments[i] = encoded + } + return strings.Join(segments, "."), nil +} + +// NATS subject to URL conversion +func NATSToURL(natsSubject string) (string, error) { + tokens := strings.Split(natsSubject, ".") + for i, token := range tokens { + // Reverse the special character encoding + decoded := strings.ReplaceAll(token, "%2E", ".") + decoded = strings.ReplaceAll(decoded, "%2A", "*") + decoded = strings.ReplaceAll(decoded, "%3E", ">") + + // Unescape remaining URL encoding + unescaped, err := url.PathUnescape(decoded) + if err != nil { + return "", fmt.Errorf("failed to unescape token: %w", err) + } + tokens[i] = unescaped + } + return "/" + strings.Join(tokens, "/"), nil +} + func main() { helpFlag := flag.Bool("help", false, "Display help information about available environment variables") flag.Parse() @@ -103,9 +146,14 @@ func main() { domainParts := strings.ReplaceAll(host, ".", "_") // Process path component - path := strings.TrimPrefix(r.URL.Path, "/") - // Replace all "." with "_" and then all "/" with ".". - subjectPath := strings.ReplaceAll(strings.ReplaceAll(path, ".", "_"), "/", ".") + path := strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, "/"), "/") + subjectPath, err := URLToNATS(path) + if err != nil { + http.Error(w, "Error converting endpoint to NATS subject", http.StatusInternalServerError) + log.Println("Could not convert endpoint to NATS subject", err) + return + + } // Build final subject subjectBase := "http" @@ -161,6 +209,43 @@ func main() { w.Write(reply.Data) }) + _, err = micro.AddService(nc, micro.Config{ + Name: "http-nats-proxy", + Version: "0.1.0", + Endpoint: µ.EndpointConfig{ + Subject: "http.*.*.proxy.>", + Handler: micro.HandlerFunc(func(natsReq micro.Request) { + // http.host.method.proxy.host.endpoint.> + httpPath := natsReq.Headers()["X-HTTP-Path"][0] + httpReqURL := fmt.Sprintf("https:/%s", strings.TrimPrefix(httpPath, "/proxy")) + httpBody := bytes.NewReader(natsReq.Data()) + httpMethod := natsReq.Headers()["X-HTTP-Method"][0] + + // Create a new request + httpReq, err := http.NewRequest(httpMethod, httpReqURL, httpBody) + if err != nil { + log.Fatal(err) + } + client := &http.Client{} + resp, err := client.Do(httpReq) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + resBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + + natsReq.Respond(resBody) + }), + }, + }) + if err != nil { + log.Fatal("Could not make NATS microservice:", err) + } + // Start the HTTP server fmt.Println("Server is running on http://localhost:8080") log.Fatal(http.ListenAndServe(":8080", nil)) |