Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tomberek/60cd89c246d436dbb5ad5fe1a0805dd0 to your computer and use it in GitHub Desktop.
Save tomberek/60cd89c246d436dbb5ad5fe1a0805dd0 to your computer and use it in GitHub Desktop.
Go server with PUT support
# 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