// CollectionOf creates a collection media type from its element media type. A collection media // type represents the content of responses that return a collection of resources such as "list" // actions. This function can be called from any place where a media type can be used. // The resulting media type identifier is built from the element media type by appending the media // type parameter "type" with value "collection". func CollectionOf(v interface{}, dsl ...func()) *design.MediaTypeDefinition { if generatedMediaTypes == nil { generatedMediaTypes = make(map[string]*design.MediaTypeDefinition) } var m *design.MediaTypeDefinition var ok bool m, ok = v.(*design.MediaTypeDefinition) if !ok { if id, ok := v.(string); ok { m = design.Design.MediaTypes[design.CanonicalIdentifier(id)] } } if m == nil { ReportError("invalid CollectionOf argument: not a media type and not a known media type identifier") return nil } id := m.Identifier mediatype, params, err := mime.ParseMediaType(id) if err != nil { ReportError("invalid media type identifier %#v: %s", id, err) return nil } hasType := false for param := range params { if param == "type" { hasType = true break } } if !hasType { params["type"] = "collection" } id = mime.FormatMediaType(mediatype, params) typeName := m.TypeName + "Collection" if mt, ok := generatedMediaTypes[typeName]; ok { // Already have a type for this collection, reuse it. return mt } mt := design.NewMediaTypeDefinition(typeName, id, func() { if mt, ok := mediaTypeDefinition(true); ok { mt.TypeName = typeName mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)} if len(dsl) > 0 { executeDSL(dsl[0], mt) } if mt.Views == nil { // If the DSL didn't create any views (or there is no DSL at all) // then inherit the views from the collection element. mt.Views = make(map[string]*design.ViewDefinition) for n, v := range m.Views { mt.Views[n] = v } } } }) // Do not execute the DSL right away, will be done last to make sure the element DSL has run // first. generatedMediaTypes[mt.TypeName] = mt return mt }
// AddMultipart creates a nested part with mediaType and a randomly generated // boundary. The returned nested part can then be used to add a text or // an attachment. // // Example: // alt, _ := part.AddMultipart("multipart/mixed") // alt.AddText("text/plain", text) // alt.AddAttachment("gopher.png", "", image) // alt.Close() func (p *Multipart) AddMultipart(mediaType string) (nested *Multipart, err error) { if !strings.HasPrefix(mediaType, "multipart") { return nil, errors.New("mail: mediaType must start with the word \"multipart\" as in multipart/mixed or multipart/alter") } if p.isClosed { return nil, ErrPartClosed } boundary := randomString(boundaryLength) // Mutlipart management var mimeType string if strings.HasPrefix(mediaType, "multipart") { mimeType = mime.FormatMediaType( mediaType, map[string]string{"boundary": boundary}, ) } else { mimeType = mediaType } // Header p.header = make(textproto.MIMEHeader) p.header["Content-Type"] = []string{mimeType} w, err := p.writer.CreatePart(p.header) if err != nil { return nil, err } nested = createPart(w, p.header, mediaType, boundary) return nested, nil }
func (post *Post) contentType() string { params := map[string]string{"type": post.Type} if post.Notification { params["rel"] = "https://tent.io/rels/notification" } return mime.FormatMediaType(MediaTypePost, params) }
func fmtContentType(s string) string { m, p, err := mime.ParseMediaType(s) if err != nil { return s } return mime.FormatMediaType(m, p) }
func Fuzz(data []byte) int { sdata := string(data) mt, params, err := mime.ParseMediaType(sdata) if err != nil { return 0 } sdata1 := mime.FormatMediaType(mt, params) mt1, params1, err := mime.ParseMediaType(sdata1) if err != nil { if err.Error() == "mime: no media type" { // https://github.com/golang/go/issues/11289 return 0 } if err.Error() == "mime: invalid media parameter" { // https://github.com/golang/go/issues/11290 return 0 } fmt.Printf("%q(%q, %+v) -> %q\n", sdata, mt, params, sdata1) panic(err) } if !fuzz.DeepEqual(mt, mt1) { fmt.Printf("%q -> %q\n", mt, mt1) panic("mediatype changed") } if !fuzz.DeepEqual(params, params1) { fmt.Printf("%+v -> %+v\n", params, params1) panic("params changed") } return 1 }
// IsError returns true if the media type is implemented via a goa struct. func (m *MediaTypeDefinition) IsError() bool { base, params, err := mime.ParseMediaType(m.Identifier) if err != nil { panic("invalid media type identifier " + m.Identifier) // bug } delete(params, "view") return mime.FormatMediaType(base, params) == ErrorMedia.Identifier }
// projectIdentifier computes the projected media type identifier by adding the "view" param. We // need the projected media type identifier to be different so that looking up projected media types // from ProjectedMediaTypes works correctly. It's also good for clients. func (m *MediaTypeDefinition) projectIdentifier(view string) string { base, params, err := mime.ParseMediaType(m.Identifier) if err != nil { base = m.Identifier } params["view"] = view return mime.FormatMediaType(base, params) }
// SetContentType returns the MIME type of the message. func (m *Message) SetContentType(mediaType string) { _, params, _ := mime.ParseMediaType(m.GetHeader("Content-Type")) if m.root != nil { params["boundary"] = m.root.Boundary() } m.SetHeader("Content-Type", mime.FormatMediaType(mediaType, params)) }
// projectIdentifier computes the projected canonical media type identifier by adding the "view" // param if the view is not the default view. func (m *MediaTypeDefinition) projectCanonical(view string) string { cano := CanonicalIdentifier(m.Identifier) base, params, _ := mime.ParseMediaType(cano) if params["view"] != "" { return cano // Already projected } params["view"] = view return mime.FormatMediaType(base, params) }
func MediaType(name string, params ...string) string { if len(params)%2 != 0 { panic(errors.New("params must be an even sized list")) } p := make(map[string]string, len(params)/2) for i := 0; i < len(params); i += 2 { p[params[i]] = params[i+1] } return mime.FormatMediaType(name, p) }
// NewMultipart modifies msg to become a multipart message and returns the root // part inside which other parts, texts and attachments can be nested. // // Example: // multipart := NewMultipart("multipart/alternative", msg) // multipart.AddPart("text/plain", text) // multipart.AddPart("text/html", html) // multipart.Close() func NewMultipart(mediaType string, msg *Message) (root *Multipart) { boundary := randomString(boundaryLength) msg.root = createPart(msg.Body, make(textproto.MIMEHeader), mediaType, boundary) _, params, _ := mime.ParseMediaType(msg.GetHeader("Content-Type")) if params == nil { params = make(map[string]string) } params["boundary"] = boundary msg.SetHeader("Content-Type", mime.FormatMediaType(mediaType, params)) return msg.root }
// CanonicalIdentifier returns the media type identifier sans suffix // which is what the DSL uses to store and lookup media types. func CanonicalIdentifier(identifier string) string { base, params, err := mime.ParseMediaType(identifier) if err != nil { return identifier } id := base if i := strings.Index(id, "+"); i != -1 { id = id[:i] } return mime.FormatMediaType(id, params) }
// MediaType implements the media type definition DSL. A media type definition describes the // representation of a resource used in a response body. // // Media types are defined with a unique identifier as defined by RFC6838. The identifier also // defines the default value for the Content-Type header of responses. The ContentType DSL allows // overridding the default as shown in the example below. // // The media type definition includes a listing of all the potential attributes that can appear in // the body. Views specify which of the attributes are actually rendered so that the same media type // definition may represent multiple rendering of a given resource representation. // // All media types must define a view named "default". This view is used to render the media type in // response bodies when no other view is specified. // // A media type definition may also define links to other media types. This is done by first // defining an attribute for the linked-to media type and then referring to that attribute in the // Links DSL. Views may then elect to render one or the other or both. Links are rendered using the // special "link" view. Media types that are linked to must define that view. Here is an example // showing all the possible media type sub-definitions: // // MediaType("application/vnd.goa.example.bottle", func() { // Description("A bottle of wine") // TypeName("BottleMedia") // Override default generated name // ContentType("application/json") // Override default Content-Type header value // Attributes(func() { // Attribute("id", Integer, "ID of bottle") // Attribute("href", String, "API href of bottle") // Attribute("account", Account, "Owner account") // Attribute("origin", Origin, "Details on wine origin") // Links(func() { // Link("account") // Defines link to Account media type // Link("origin", "tiny") // Set view used to render link if not "link" // }) // Required("id", "href") // }) // View("default", func() { // Attribute("id") // Attribute("href") // Attribute("links") // Renders links // }) // View("extended", func() { // Attribute("id") // Attribute("href") // Attribute("account") // Renders account inline // Attribute("origin") // Renders origin inline // Attribute("links") // Renders links // }) // }) // // This function returns the media type definition so it can be referred to throughout the apidsl. func MediaType(identifier string, apidsl func()) *design.MediaTypeDefinition { if design.Design.MediaTypes == nil { design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition) } if !dslengine.IsTopLevelDefinition() { dslengine.IncompatibleDSL() return nil } // Validate Media Type identifier, params, err := mime.ParseMediaType(identifier) if err != nil { dslengine.ReportError("invalid media type identifier %#v: %s", identifier, err) // We don't return so that other errors may be // captured in this one run. identifier = "text/plain" } canonicalID := design.CanonicalIdentifier(identifier) // Validate that media type identifier doesn't clash if _, ok := design.Design.MediaTypes[canonicalID]; ok { dslengine.ReportError("media type %#v with canonical identifier %#v is defined twice", identifier, canonicalID) return nil } identifier = mime.FormatMediaType(identifier, params) lastPart := identifier lastPartIndex := strings.LastIndex(identifier, "/") if lastPartIndex > -1 { lastPart = identifier[lastPartIndex+1:] } plusIndex := strings.Index(lastPart, "+") if plusIndex > 0 { lastPart = lastPart[:plusIndex] } lastPart = strings.TrimPrefix(lastPart, "vnd.") elems := strings.Split(lastPart, ".") for i, e := range elems { elems[i] = strings.Title(e) } typeName := strings.Join(elems, "") if typeName == "" { mediaTypeCount++ typeName = fmt.Sprintf("MediaType%d", mediaTypeCount) } // Now save the type in the API media types map mt := design.NewMediaTypeDefinition(typeName, identifier, apidsl) design.Design.MediaTypes[canonicalID] = mt return mt }
// SetBodyWithCharset translates and sets the body according to given charset. // // Header field Content-Transfer-Encoding is set to DefaultTransferEncoding. // Header field Content-Type is set according to charset. // All lines are modified to ensure CRLF. // // Use SetBody to use default character encoding. func (m *Message) SetBodyWithCharset(charset, body string) error { m.Header.Set(HEADER_CONTENT_TRANSFER_ENCODING, DefaultTransferEncoding) m.Header.Set(HEADER_CONTENT_TYPE, mime.FormatMediaType( "text/plain", map[string]string{"charset": DefaultCharset}, )) bytes, err := StringToBody(body, DefaultCharset) if err != nil { return err } m.body = bytes m.Header.Set(HEADER_BODY, fmt.Sprintf("%d", len(bytes))) return nil }
func (ar assertResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) { t := asserts.MediaType if ar.bundle { t = mime.FormatMediaType(t, map[string]string{"bundle": "y"}) } w.Header().Set("Content-Type", t) w.Header().Set("X-Ubuntu-Assertions-Count", strconv.Itoa(len(ar.assertions))) enc := asserts.NewEncoder(w) for _, a := range ar.assertions { err := enc.Encode(a) if err != nil { logger.Noticef("unable to write encoded assertion into response: %v", err) break } } }
// CollectionOf creates a collection media type from its element media type. A collection media // type represents the content of responses that return a collection of resources such as "list" // actions. This function can be called from any place where a media type can be used. // The resulting media type identifier is built from the element media type by appending the media // type parameter "type" with value "collection". func CollectionOf(m *design.MediaTypeDefinition, dsl ...func()) *design.MediaTypeDefinition { id := m.Identifier mediatype, params, err := mime.ParseMediaType(id) if err != nil { ReportError("invalid media type identifier %#v: %s", id, err) return nil } slash := strings.Index(mediatype, "/") if slash == -1 { mediatype += "/json" } hasType := false for param := range params { if param == "type" { hasType = true break } } if !hasType { params["type"] = "collection" } id = mime.FormatMediaType(mediatype, params) typeName := m.TypeName + "Collection" mt := design.NewMediaTypeDefinition(typeName, id, func() { if mt, ok := mediaTypeDefinition(true); ok { mt.TypeName = typeName mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)} if len(dsl) > 0 { executeDSL(dsl[0], mt) } } }) if executeDSL(mt.DSL, mt) { design.Design.MediaTypes[id] = mt } return mt }
// MediaType implements the media type definition DSL. A media type definition describes the // representation of a resource used in a response body. This includes listing all the *potential* // resource attributes that can appear in the body. Views specify which of the attributes are // actually rendered so that the same media type definition may represent multiple rendering of a // given resource representation. // // All media types must define a view named "default". This view is used to render the media type in // response bodies when no other view is specified. // // A media type definition may also define links to other media types. This is done by first // defining an attribute for the linked-to media type and then referring to that attribute in the // Links DSL. Views may then elect to render one or the other or both. Links are rendered using the // special "link" view. Media types that are linked to must define that view. Here is an example // showing all the possible media type sub-definitions: // // MediaType("application/vnd.goa.example.bottle", func() { // Description("A bottle of wine") // Attributes(func() { // Attribute("id", Integer, "ID of bottle") // Attribute("href", String, "API href of bottle") // Attribute("account", Account, "Owner account") // Attribute("origin", Origin, "Details on wine origin") // Links(func() { // Link("account") // Defines a link to the Account media type // Link("origin", "tiny") // Overrides the default view used to render links // }) // Required("id", "href") // }) // View("default", func() { // Attribute("id") // Attribute("href") // Attribute("links") // Default view renders links // }) // View("extended", func() { // Attribute("id") // Attribute("href") // Attribute("account") // Extended view renders account inline // Attribute("origin") // Extended view renders origin inline // Attribute("links") // Extended view also renders links // }) // }) // // This function returns the media type definition so it can be referred to throughout the DSL. func MediaType(identifier string, dsl func()) *design.MediaTypeDefinition { if design.Design == nil { InitDesign() } if design.Design.MediaTypes == nil { design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition) } if topLevelDefinition(true) { identifier, params, err := mime.ParseMediaType(identifier) if err != nil { ReportError("invalid media type identifier %#v: %s", identifier, err) } slash := strings.Index(identifier, "/") if slash == -1 { identifier += "/json" } identifier = mime.FormatMediaType(identifier, params) elems := strings.Split(identifier, ".") elems = strings.Split(elems[len(elems)-1], "/") elems = strings.Split(elems[0], "+") typeName := inflect.Camelize(elems[0]) if typeName == "" { mediaTypeCount++ typeName = fmt.Sprintf("MediaType%d", mediaTypeCount) } if _, ok := design.Design.MediaTypes[identifier]; ok { ReportError("media type %#v is defined twice", identifier) return nil } mt := design.NewMediaTypeDefinition(typeName, identifier, dsl) design.Design.MediaTypes[identifier] = mt return mt } return nil }
// formatMediaType serializes mediatype t and the parameters // param as a media type conforming to RFC 2045 and RFC 2616. // The type and parameter names are written in lower-case. // When any of the arguments result in a standard violation then // formatMediaType returns the empty string. func formatMediaType(t string, param map[string]string) string { return mime.FormatMediaType(t, param) }
// ContentType returns an RFC 2045 compatible internet media type string. func (res *Response) ContentType() string { return mime.FormatMediaType(res.MediaType, map[string]string{"charset": res.Charset}) }
// CollectionOf creates a collection media type from its element media type. A collection media // type represents the content of responses that return a collection of resources such as "list" // actions. This function can be called from any place where a media type can be used. // The resulting media type identifier is built from the element media type by appending the media // type parameter "type" with value "collection". func CollectionOf(v interface{}, apidsl ...func()) *design.MediaTypeDefinition { var m *design.MediaTypeDefinition var ok bool m, ok = v.(*design.MediaTypeDefinition) if !ok { if id, ok := v.(string); ok { m = design.Design.MediaTypes[design.CanonicalIdentifier(id)] } } if m == nil { dslengine.ReportError("invalid CollectionOf argument: not a media type and not a known media type identifier") // don't return nil to avoid panics, the error will get reported at the end return design.NewMediaTypeDefinition("InvalidCollection", "text/plain", nil) } id := m.Identifier mediatype, params, err := mime.ParseMediaType(id) if err != nil { dslengine.ReportError("invalid media type identifier %#v: %s", id, err) // don't return nil to avoid panics, the error will get reported at the end return design.NewMediaTypeDefinition("InvalidCollection", "text/plain", nil) } hasType := false for param := range params { if param == "type" { hasType = true break } } if !hasType { params["type"] = "collection" } id = mime.FormatMediaType(mediatype, params) canonical := design.CanonicalIdentifier(id) if mt, ok := design.GeneratedMediaTypes[canonical]; ok { // Already have a type for this collection, reuse it. return mt } mt := design.NewMediaTypeDefinition("", id, func() { if mt, ok := mediaTypeDefinition(); ok { // Cannot compute collection type name before element media type DSL has executed // since the DSL may modify element type name via the TypeName function. mt.TypeName = m.TypeName + "Collection" mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)} if len(apidsl) > 0 { dslengine.Execute(apidsl[0], mt) } if mt.Views == nil { // If the apidsl didn't create any views (or there is no apidsl at all) // then inherit the views from the collection element. mt.Views = make(map[string]*design.ViewDefinition) for n, v := range m.Views { mt.Views[n] = v } } } }) // Do not execute the apidsl right away, will be done last to make sure the element apidsl has run // first. design.GeneratedMediaTypes[canonical] = mt return mt }
// ServeHTTP streams a zip archive of all the files "under" // zh.root. That is, all the files pointed by file permanodes, // which are directly members of zh.root or recursively down // directory permanodes and permanodes members. // To build the fullpath of a file in a collection, it uses // the collection title if present, its blobRef otherwise, as // a directory name. func (zh *zipHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // TODO: use http.ServeContent, so Range requests work and downloads can be resumed. // Will require calculating the zip length once first (ideally as cheaply as possible, // with dummy counting writer and dummy all-zero-byte-files of a fixed size), // and then making a dummy ReadSeeker for ServeContent that can seek to the end, // and then seek back to the beginning, but then seeks forward make it remember // to skip that many bytes from the archive/zip writer when answering Reads. if !httputil.IsGet(req) { http.Error(rw, "Invalid method", http.StatusMethodNotAllowed) return } bf, err := zh.blobList("/", zh.root) if err != nil { log.Printf("Could not serve zip for %v: %v", zh.root, err) http.Error(rw, "Server error", http.StatusInternalServerError) return } blobFiles := renameDuplicates(bf) // TODO(mpl): streaming directly won't work on appengine if the size goes // over 32 MB. Deal with that. h := rw.Header() h.Set("Content-Type", "application/zip") filename := zh.filename if filename == "" { filename = "download.zip" } h.Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename": filename})) zw := zip.NewWriter(rw) etag := sha1.New() for _, file := range blobFiles { etag.Write([]byte(file.blobRef.String())) } h.Set("Etag", fmt.Sprintf(`"%x"`, etag.Sum(nil))) for _, file := range blobFiles { fr, err := schema.NewFileReader(zh.storageSeekFetcher(), file.blobRef) if err != nil { log.Printf("Can not add %v in zip, not a file: %v", file.blobRef, err) http.Error(rw, "Server error", http.StatusInternalServerError) return } f, err := zw.CreateHeader( &zip.FileHeader{ Name: file.path, Method: zip.Store, }) if err != nil { log.Printf("Could not create %q in zip: %v", file.path, err) http.Error(rw, "Server error", http.StatusInternalServerError) return } _, err = io.Copy(f, fr) fr.Close() if err != nil { log.Printf("Could not zip %q: %v", file.path, err) return } } err = zw.Close() if err != nil { log.Printf("Could not close zipwriter: %v", err) return } }
// MediaType implements the media type definition DSL. A media type definition describes the // representation of a resource used in a response body. This includes listing all the *potential* // resource attributes that can appear in the body. Views specify which of the attributes are // actually rendered so that the same media type definition may represent multiple rendering of a // given resource representation. // // All media types must define a view named "default". This view is used to render the media type in // response bodies when no other view is specified. // // A media type definition may also define links to other media types. This is done by first // defining an attribute for the linked-to media type and then referring to that attribute in the // Links DSL. Views may then elect to render one or the other or both. Links are rendered using the // special "link" view. Media types that are linked to must define that view. Here is an example // showing all the possible media type sub-definitions: // // MediaType("application/vnd.goa.example.bottle", func() { // Description("A bottle of wine") // APIVersion("1.0") // Attributes(func() { // Attribute("id", Integer, "ID of bottle") // Attribute("href", String, "API href of bottle") // Attribute("account", Account, "Owner account") // Attribute("origin", Origin, "Details on wine origin") // Links(func() { // Link("account") // Defines a link to the Account media type // Link("origin", "tiny") // Overrides the default view used to render links // }) // Required("id", "href") // }) // View("default", func() { // Attribute("id") // Attribute("href") // Attribute("links") // Default view renders links // }) // View("extended", func() { // Attribute("id") // Attribute("href") // Attribute("account") // Extended view renders account inline // Attribute("origin") // Extended view renders origin inline // Attribute("links") // Extended view also renders links // }) // }) // // This function returns the media type definition so it can be referred to throughout the DSL. func MediaType(identifier string, dsl func()) *design.MediaTypeDefinition { if design.Design == nil { InitDesign() } if design.Design.MediaTypes == nil { design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition) } if topLevelDefinition(true) { // Validate Media Type identifier, params, err := mime.ParseMediaType(identifier) if err != nil { ReportError("invalid media type identifier %#v: %s", identifier, err) // We don't return so that other errors may be // captured in this one run. identifier = "plain/text" } canonicalID := design.CanonicalIdentifier(identifier) // Validate that media type identifier doesn't clash if _, ok := design.Design.MediaTypes[canonicalID]; ok { ReportError("media type %#v is defined twice", identifier) return nil } parts := strings.Split(identifier, "+") // Make sure it has the `+json` suffix (TBD update when goa supports other encodings) if len(parts) > 1 { parts = parts[1:] found := false for _, part := range parts { if part == "json" { found = true break } } if !found { identifier += "+json" } } identifier = mime.FormatMediaType(identifier, params) // Concoct a Go type name from the identifier, should it be possible to set it in the DSL? // pros: control the type name generated, cons: not needed in DSL, adds one more thing to worry about lastPart := identifier lastPartIndex := strings.LastIndex(identifier, "/") if lastPartIndex > -1 { lastPart = identifier[lastPartIndex+1:] } plusIndex := strings.Index(lastPart, "+") if plusIndex > 0 { lastPart = lastPart[:plusIndex] } lastPart = strings.TrimPrefix(lastPart, "vnd.") elems := strings.Split(lastPart, ".") for i, e := range elems { elems[i] = strings.Title(e) } typeName := strings.Join(elems, "") if typeName == "" { mediaTypeCount++ typeName = fmt.Sprintf("MediaType%d", mediaTypeCount) } // Now save the type in the API media types map mt := design.NewMediaTypeDefinition(typeName, identifier, dsl) design.Design.MediaTypes[canonicalID] = mt return mt } return nil }
func (pm *ParsedMimeType) String() string { return mime.FormatMediaType(pm.MediaType, pm.Params) }