diff options
-rw-r--r-- | main.go | 85 |
1 files changed, 56 insertions, 29 deletions
@@ -1,27 +1,66 @@ package main import ( + "flag" "fmt" "io/ioutil" "log" "net/http" + "os" "strings" "time" "github.com/nats-io/nats.go" ) +// printHelp outputs the usage information. +func printHelp() { + fmt.Println("Usage: HTTP-to-NATS proxy server") + fmt.Println("\nThe following environment variables are supported:") + fmt.Println(" NATS_URL - NATS connection URL (default: nats://127.0.0.1:4222)") + fmt.Println(" NATS_USER - NATS username for authentication (optional)") + fmt.Println(" NATS_PASSWORD - NATS password for authentication (optional)") + fmt.Println(" HTTP_PORT - HTTP port to listen on (default: 8080)") +} + func main() { - // Connect to the NATS server - nc, err := nats.Connect(nats.DefaultURL) + helpFlag := flag.Bool("help", false, "Display help information about available environment variables") + flag.Parse() + + if *helpFlag { + printHelp() + os.Exit(0) + } + + // Read NATS connection info from environment variables + natsURL := os.Getenv("NATS_URL") + if natsURL == "" { + natsURL = nats.DefaultURL // defaults to "nats://127.0.0.1:4222" + } + natsUser := os.Getenv("NATS_USER") + natsPassword := os.Getenv("NATS_PASSWORD") + + // Read HTTP port from environment variables + httpPort := os.Getenv("HTTP_PORT") + if httpPort == "" { + httpPort = "8080" + } + + // Set up NATS connection options if authentication info is provided. + var opts []nats.Option + if natsUser != "" && natsPassword != "" { + opts = append(opts, nats.UserInfo(natsUser, natsPassword)) + } + + nc, err := nats.Connect(natsURL, opts...) if err != nil { log.Fatal("Error connecting to NATS:", err) } defer nc.Close() - // HTTP handler to proxy all requests to NATS + // HTTP handler function http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - // Read the entire request body (if any) + // Read the entire request body body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Error reading request body", http.StatusInternalServerError) @@ -29,55 +68,43 @@ func main() { } defer r.Body.Close() - // Create the NATS subject. - // Remove the leading slash from the path and replace remaining slashes with dots. - // The subject is prefixed with "http.<method>.", where <method> is lower-case. - path := strings.TrimPrefix(r.URL.Path, "/") - subjectPath := strings.ReplaceAll(path, "/", ".") - subject := fmt.Sprintf("http.%s.%s", strings.ToLower(r.Method), subjectPath) - - log.Println("Forwarding HTTP", r.Method, "request on", r.URL.Path, "to NATS subject:", subject) - - // Create a new NATS message with the HTTP request body + // Drop the leading slash, replace remaining slashes with dots to form the subject + subject := strings.ReplaceAll(strings.TrimPrefix(r.URL.Path, "/"), "/", ".") + // Prefix the subject with "http." to follow the convention msg := nats.Msg{ - Subject: subject, + Subject: fmt.Sprintf("http.%s", subject), Data: body, Header: nats.Header{}, } - // Copy over all the HTTP request headers to the NATS message headers + log.Println("Forwarding request to subject:", msg.Subject) + + // Copy HTTP headers to the NATS message headers for key, values := range r.Header { for _, value := range values { msg.Header.Add(key, value) } } - // Add additional HTTP meta-data as headers - msg.Header.Set("X-HTTP-Method", r.Method) - msg.Header.Set("X-HTTP-Path", r.URL.Path) - msg.Header.Set("X-HTTP-Query", r.URL.RawQuery) - msg.Header.Set("X-Remote-Addr", r.RemoteAddr) - - // Send the NATS request and wait synchronously for a reply (timeout: 30 seconds) + // Send request to NATS and wait for reply (timeout: 30 seconds) reply, err := nc.RequestMsg(&msg, 30*time.Second) if err != nil { http.Error(w, "Error processing request", http.StatusInternalServerError) - log.Println("NATS request error:", err) + log.Println("Error processing the request:", err) return } - // Set any response headers from the NATS reply on the HTTP response + // Copy headers from the NATS reply to the HTTP response for key, values := range reply.Header { for _, value := range values { w.Header().Add(key, value) } } - // Write the reply body (from NATS) back to the HTTP client + // Write NATS reply body to HTTP response w.Write(reply.Data) }) - // Start the HTTP server - fmt.Println("Server is running on http://localhost:8080") - log.Fatal(http.ListenAndServe(":8080", nil)) + fmt.Println("Server is running on http://localhost:" + httpPort) + log.Fatal(http.ListenAndServe(":"+httpPort, nil)) } |