Created
May 1, 2025 15:21
-
-
Save tomberek/60cd89c246d436dbb5ad5fe1a0805dd0 to your computer and use it in GitHub Desktop.
Go server with PUT support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# built with AI | |
package main | |
import ( | |
"fmt" | |
"io" | |
"net/http" | |
"os" | |
"path/filepath" | |
"strings" | |
) | |
const ( | |
storageDir = "./storage" | |
redirectBase = "/redirected" | |
) | |
func main() { | |
// Create storage directory if it doesn't exist | |
if err := os.MkdirAll(storageDir, 0755); err != nil { | |
fmt.Printf("Error creating storage directory: %v\n", err) | |
return | |
} | |
mux := http.NewServeMux() | |
mux.HandleFunc("/", handler) | |
fmt.Println("Server starting on :8080...") | |
if err := http.ListenAndServe(":8080", mux); err != nil { | |
fmt.Printf("Server error: %v\n", err) | |
} | |
} | |
func handler(w http.ResponseWriter, r *http.Request) { | |
// Skip redirection if: | |
// 1. Request is already under our redirect base path | |
// 2. This is a request to the root redirect path | |
if strings.HasPrefix(r.URL.Path, redirectBase) || r.URL.Path == redirectBase { | |
handleFileOperations(w, r) | |
return | |
} | |
// Redirect all other requests | |
newPath := redirectBase + r.URL.Path | |
http.Redirect(w, r, newPath, http.StatusPermanentRedirect) | |
} | |
func handleFileOperations(w http.ResponseWriter, r *http.Request) { | |
// Remove redirect prefix to get the actual file path | |
path := strings.TrimPrefix(r.URL.Path, redirectBase) | |
if path == "" { | |
path = "/" | |
} | |
// Secure file path construction | |
filePath := filepath.Join(storageDir, filepath.Clean(path)) | |
switch r.Method { | |
case http.MethodGet, http.MethodHead: | |
serveFile(w, r, filePath) | |
case http.MethodPut: | |
putFile(w, r, filePath) | |
default: | |
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) | |
} | |
} | |
func serveFile(w http.ResponseWriter, r *http.Request, filePath string) { | |
file, err := os.Open(filePath) | |
if err != nil { | |
if os.IsNotExist(err) { | |
http.Error(w, "File not found", http.StatusNotFound) | |
} else { | |
http.Error(w, "Error reading file", http.StatusInternalServerError) | |
} | |
return | |
} | |
defer file.Close() | |
stat, err := file.Stat() | |
if err != nil { | |
http.Error(w, "Error getting file info", http.StatusInternalServerError) | |
return | |
} | |
w.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size())) | |
if r.Method == http.MethodGet { | |
if _, err := io.Copy(w, file); err != nil { | |
http.Error(w, "Error sending file content", http.StatusInternalServerError) | |
} | |
} | |
} | |
func putFile(w http.ResponseWriter, r *http.Request, filePath string) { | |
if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { | |
http.Error(w, "Error creating directory", http.StatusInternalServerError) | |
return | |
} | |
file, err := os.Create(filePath) | |
if err != nil { | |
http.Error(w, "Error creating file", http.StatusInternalServerError) | |
return | |
} | |
defer file.Close() | |
if _, err := io.Copy(file, r.Body); err != nil { | |
http.Error(w, "Error writing file", http.StatusInternalServerError) | |
return | |
} | |
w.WriteHeader(http.StatusCreated) | |
fmt.Fprintf(w, "File created/updated at %s", r.URL.Path) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment