Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement ocs to http status code mapping #696

Merged
merged 1 commit into from
Apr 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions internal/http/services/owncloud/ocs/ocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import (
"github.com/mitchellh/mapstructure"
)

const (
apiV1 = "v1.php"
apiV2 = "v2.php"
)

func init() {
global.Register("ocs", New)
}
Expand Down Expand Up @@ -86,13 +91,11 @@ func (s *svc) Handler() http.Handler {

log.Debug().Str("head", head).Str("tail", r.URL.Path).Msg("ocs routing")

// TODO v2 uses a status code mapper
// see https://github.com/owncloud/core/commit/bacf1603ffd53b7a5f73854d1d0ceb4ae545ce9f#diff-262cbf0df26b45bad0cf00d947345d9c
if head == "v1.php" || head == "v2.php" {
s.V1Handler.Handler().ServeHTTP(w, r)
if !(head == apiV1 || head == apiV2) {
response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "Not found", nil)
return
}

response.WriteOCSError(w, r, response.MetaNotFound.StatusCode, "Not found", nil)
ctx := response.WithAPIVersion(r.Context(), head)
s.V1Handler.Handler().ServeHTTP(w, r.WithContext(ctx))
})
}
129 changes: 96 additions & 33 deletions internal/http/services/owncloud/ocs/response/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package response

import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"net/http"
Expand All @@ -28,6 +30,16 @@ import (
"github.com/cs3org/reva/pkg/appctx"
)

type key int

const (
apiVersionKey key = 1
)

var (
defaultStatusCodeMapper = OcsV2StatusCodes
)

// Response is the top level response structure
type Response struct {
OCS *Payload `json:"ocs"`
Expand Down Expand Up @@ -137,54 +149,28 @@ func WriteOCSData(w http.ResponseWriter, r *http.Request, m *Meta, d interface{}

// WriteOCSResponse handles writing ocs responses in json and xml
func WriteOCSResponse(w http.ResponseWriter, r *http.Request, res *Response, err error) {
var encoded []byte

if err != nil {
appctx.GetLogger(r.Context()).Error().Err(err).Msg(res.OCS.Meta.Message)
}

var encoder func(*Payload) ([]byte, error)
if r.URL.Query().Get("format") == "json" {
w.Header().Set("Content-Type", "application/json")
encoded, err = json.Marshal(res)
encoder = encodeJSON
} else {
w.Header().Set("Content-Type", "application/xml")
_, err = w.Write([]byte(xml.Header))
if err != nil {
appctx.GetLogger(r.Context()).Error().Err(err).Msg("error writing xml header")
w.WriteHeader(http.StatusInternalServerError)
return
}
encoded, err = xml.Marshal(res.OCS)
encoder = encodeXML
}
encoded, err := encoder(res.OCS)
if err != nil {
appctx.GetLogger(r.Context()).Error().Err(err).Msg("error encoding ocs response")
w.WriteHeader(http.StatusInternalServerError)
return
}

// TODO map error for v2 only?
// see https://github.com/owncloud/core/commit/bacf1603ffd53b7a5f73854d1d0ceb4ae545ce9f#diff-262cbf0df26b45bad0cf00d947345d9c
switch res.OCS.Meta.StatusCode {
case MetaNotFound.StatusCode:
w.WriteHeader(http.StatusNotFound)
case MetaServerError.StatusCode:
w.WriteHeader(http.StatusInternalServerError)
case MetaUnknownError.StatusCode:
w.WriteHeader(http.StatusInternalServerError)
case MetaUnauthorized.StatusCode:
w.WriteHeader(http.StatusUnauthorized)
case 100:
w.WriteHeader(http.StatusOK)
case 104:
w.WriteHeader(http.StatusForbidden)
default:
// any 2xx, 4xx and 5xx will be used as is
if res.OCS.Meta.StatusCode >= 200 && res.OCS.Meta.StatusCode < 600 {
w.WriteHeader(res.OCS.Meta.StatusCode)
} else {
w.WriteHeader(http.StatusBadRequest)
}
}
m := statusCodeMapper(r.Context())
statusCode := m(res.OCS.Meta)
w.WriteHeader(statusCode)

_, err = w.Write(encoded)
if err != nil {
Expand All @@ -203,3 +189,80 @@ func UserIDToString(userID *user.UserId) string {
}
return userID.OpaqueId + "@" + userID.Idp
}

func encodeXML(payload *Payload) ([]byte, error) {
marshalled, err := xml.Marshal(payload)
if err != nil {
return nil, err
}
b := new(bytes.Buffer)
b.Write([]byte(xml.Header))
b.Write(marshalled)
return b.Bytes(), nil
}

func encodeJSON(payload *Payload) ([]byte, error) {
return json.Marshal(payload)
}

// OcsV1StatusCodes returns the http status codes for the OCS API v1.
func OcsV1StatusCodes(meta *Meta) int {
return http.StatusOK
}

// OcsV2StatusCodes maps the OCS codes to http status codes for the ocs API v2.
func OcsV2StatusCodes(meta *Meta) int {
sc := meta.StatusCode
switch sc {
case MetaNotFound.StatusCode:
return http.StatusNotFound
case MetaUnknownError.StatusCode:
fallthrough
case MetaServerError.StatusCode:
return http.StatusInternalServerError
case MetaUnauthorized.StatusCode:
return http.StatusUnauthorized
case 100:
return http.StatusOK
}
// any 2xx, 4xx and 5xx will be used as is
if sc >= 200 && sc < 600 {
return sc
}

// any error codes > 100 are treated as client errors
if sc > 100 && sc < 200 {
return http.StatusBadRequest
}

// TODO change this status code?
return http.StatusOK
}

// WithAPIVersion puts the api version in the context.
func WithAPIVersion(parent context.Context, version string) context.Context {
return context.WithValue(parent, apiVersionKey, version)
}

// APIVersion retrieves the api version from the context.
func APIVersion(ctx context.Context) string {
value := ctx.Value(apiVersionKey)
if value != nil {
return value.(string)
}
return ""
}

func statusCodeMapper(ctx context.Context) func(*Meta) int {
version := APIVersion(ctx)
var mapper func(*Meta) int
switch version {
case "v1.php":
mapper = OcsV1StatusCodes
case "v2.php":
mapper = OcsV2StatusCodes
default:
mapper = defaultStatusCodeMapper
}
return mapper
}