package s3err import ( "bytes" "encoding/xml" "fmt" "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" "github.com/gorilla/mux" "github.com/seaweedfs/seaweedfs/weed/glog" "net/http" "strconv" "strings" "time" ) type mimeType string const ( mimeNone mimeType = "" MimeXML mimeType = "application/xml" ) func WriteAwsXMLResponse(w http.ResponseWriter, r *http.Request, statusCode int, result interface{}) { var bytesBuffer bytes.Buffer err := xmlutil.BuildXML(result, xml.NewEncoder(&bytesBuffer)) if err != nil { WriteErrorResponse(w, r, ErrInternalError) return } WriteResponse(w, r, statusCode, bytesBuffer.Bytes(), MimeXML) } func WriteXMLResponse(w http.ResponseWriter, r *http.Request, statusCode int, response interface{}) { WriteResponse(w, r, statusCode, EncodeXMLResponse(response), MimeXML) } func WriteEmptyResponse(w http.ResponseWriter, r *http.Request, statusCode int) { WriteResponse(w, r, statusCode, []byte{}, mimeNone) PostLog(r, statusCode, ErrNone) } func WriteErrorResponse(w http.ResponseWriter, r *http.Request, errorCode ErrorCode) { vars := mux.Vars(r) bucket := vars["bucket"] object := vars["object"] if strings.HasPrefix(object, "/") { object = object[1:] } apiError := GetAPIError(errorCode) errorResponse := getRESTErrorResponse(apiError, r.URL.Path, bucket, object) encodedErrorResponse := EncodeXMLResponse(errorResponse) WriteResponse(w, r, apiError.HTTPStatusCode, encodedErrorResponse, MimeXML) PostLog(r, apiError.HTTPStatusCode, errorCode) } func getRESTErrorResponse(err APIError, resource string, bucket, object string) RESTErrorResponse { return RESTErrorResponse{ Code: err.Code, BucketName: bucket, Key: object, Message: err.Description, Resource: resource, RequestID: fmt.Sprintf("%d", time.Now().UnixNano()), } } // Encodes the response headers into XML format. func EncodeXMLResponse(response interface{}) []byte { var bytesBuffer bytes.Buffer bytesBuffer.WriteString(xml.Header) e := xml.NewEncoder(&bytesBuffer) e.Encode(response) return bytesBuffer.Bytes() } func setCommonHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Set("x-amz-request-id", fmt.Sprintf("%d", time.Now().UnixNano())) w.Header().Set("Accept-Ranges", "bytes") if r.Header.Get("Origin") != "" { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Expose-Headers", "*") w.Header().Set("Access-Control-Allow-Credentials", "true") } } func WriteResponse(w http.ResponseWriter, r *http.Request, statusCode int, response []byte, mType mimeType) { setCommonHeaders(w, r) if response != nil { w.Header().Set("Content-Length", strconv.Itoa(len(response))) } if mType != mimeNone { w.Header().Set("Content-Type", string(mType)) } w.WriteHeader(statusCode) if response != nil { glog.V(4).Infof("status %d %s: %s", statusCode, mType, string(response)) _, err := w.Write(response) if err != nil { glog.V(0).Infof("write err: %v", err) } w.(http.Flusher).Flush() } } // If none of the http routes match respond with MethodNotAllowed func NotFoundHandler(w http.ResponseWriter, r *http.Request) { glog.V(0).Infof("unsupported %s %s", r.Method, r.RequestURI) WriteErrorResponse(w, r, ErrMethodNotAllowed) }