Skip to content
File

Blob: src/workerd/util/mimetype.h

cpp231 lines
1// Copyright (c) 2023 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4#pragma once
5 
6#include <workerd/jsg/memory.h>
7#include <workerd/util/strings.h>
8 
9#include <kj/common.h>
10#include <kj/map.h>
11#include <kj/string.h>
12 
13namespace workerd {
14 
15template <size_t>
16class StringBuffer;
17class MimeType;
18class ConstMimeType final {
19 public:
20 constexpr ConstMimeType(kj::StringPtr type, kj::StringPtr subtype)
21 : type_(type),
22 subtype_(subtype) {}
23 
24 constexpr kj::StringPtr type() const {
25 return type_;
26 }
27 constexpr kj::StringPtr subtype() const {
28 return subtype_;
29 }
30 
31 constexpr bool operator==(const ConstMimeType& other) const {
32 return this == &other || (type_ == other.type_ && subtype_ == other.subtype_);
33 }
34 
35 bool operator==(const MimeType& other) const;
36 operator MimeType() const;
37 MimeType clone() const;
38 
39 inline kj::String toString() const {
40 return kj::str(type_, "/", subtype_);
41 }
42 
43 inline kj::String essence() const {
44 return toString();
45 }
46 
47 private:
48 kj::StringPtr type_;
49 kj::StringPtr subtype_;
50};
51 
52template <typename T>
53concept IsMimeType = kj::isSameType<T, MimeType>() || kj::isSameType<T, ConstMimeType>();
54static_assert(IsMimeType<MimeType>);
55static_assert(IsMimeType<ConstMimeType>);
56 
57class MimeType final {
58 public:
59 using MimeParams = kj::HashMap<kj::String, kj::String>;
60 
61 enum ParseOptions {
62 DEFAULT,
63 IGNORE_PARAMS,
64 };
65 
66 // Returning nullptr implies that the input is not a valid mime type construction.
67 // If the ParseOptions::IGNORE_PARAMS option is set then the mime type parameters
68 // will be ignored and will not be included in the parsed result.
69 static kj::Maybe<MimeType> tryParse(
70 kj::ArrayPtr<const char> input, ParseOptions options = ParseOptions::DEFAULT);
71 
72 // Asserts if the input could not be parsed as a valid MimeType. tryParse should
73 // be preferred for most cases.
74 static MimeType parse(kj::StringPtr input, ParseOptions options = ParseOptions::DEFAULT);
75 
76 explicit MimeType(
77 kj::StringPtr type, kj::StringPtr subtype, kj::Maybe<MimeParams> params = kj::none);
78 explicit MimeType(kj::String type, kj::String subtype, kj::Maybe<MimeParams> params = kj::none);
79 
80 MimeType(MimeType&&) = default;
81 MimeType& operator=(MimeType&&) = default;
82 KJ_DISALLOW_COPY(MimeType);
83 
84 kj::StringPtr type() const;
85 kj::StringPtr subtype() const;
86 
87 const MimeParams& params() const;
88 
89 bool setType(kj::StringPtr type);
90 bool setSubtype(kj::StringPtr type);
91 bool addParam(kj::ArrayPtr<const char> name, kj::ArrayPtr<const char> value);
92 void eraseParam(kj::StringPtr name);
93 
94 // Returns only the type/subtype
95 kj::String essence() const;
96 
97 // Returns the type/subtype and all params.
98 kj::String toString() const;
99 
100 kj::String paramsToString() const;
101 
102 // Copy this MimeType. If the IGNORE_PARAMS option is set the clone
103 // will copy only the type and subtype and will omit all of the parameters.
104 MimeType clone(ParseOptions options = ParseOptions::DEFAULT) const;
105 
106 // Compares only the essence of the MimeType (type and subtype). Ignores
107 // parameters in the comparison.
108 bool operator==(const MimeType& other) const;
109 
110 operator kj::String() const;
111 
112 template <IsMimeType T>
113 static constexpr bool isXml(const T& mimeType) {
114 auto type = mimeType.type();
115 auto subtype = mimeType.subtype();
116 // Bare "xml" subtype is only valid for text/xml and application/xml.
117 // The "+xml" structured syntax suffix (RFC 6838 §4.2.8, RFC 6839) indicates
118 // XML-based content regardless of top-level type (e.g. image/svg+xml).
119 return ((type == "text" || type == "application") && subtype == "xml") ||
120 subtype.endsWith("+xml");
121 }
122 
123 template <IsMimeType T>
124 static constexpr bool isJson(const T& mimeType) {
125 auto type = mimeType.type();
126 auto subtype = mimeType.subtype();
127 return (type == "text" || type == "application") &&
128 (subtype == "json" || subtype.endsWith("+json"));
129 }
130 
131 template <IsMimeType T>
132 static constexpr bool isFont(const T& mimeType) {
133 auto type = mimeType.type();
134 auto subtype = mimeType.subtype();
135 return (type == "font" || type == "application") &&
136 (subtype.startsWith("font-") || subtype.startsWith("x-font-"));
137 }
138 
139 template <IsMimeType T>
140 static constexpr bool isJavascript(const T& mimeType) {
141 return JAVASCRIPT == mimeType || XJAVASCRIPT == mimeType || TEXT_JAVASCRIPT == mimeType;
142 }
143 
144 template <IsMimeType T>
145 static constexpr bool isText(const T& mimeType) {
146 auto type = mimeType.type();
147 auto subtype = mimeType.subtype();
148 return type == "text" || isXml(mimeType) || isJson(mimeType) || isJavascript(mimeType) ||
149 (type == "application" && subtype == "dns-json");
150 }
151 
152 template <IsMimeType T>
153 static constexpr bool isImage(const T& mimeType) {
154 return mimeType.type() == "image";
155 }
156 
157 template <IsMimeType T>
158 static constexpr bool isVideo(const T& mimeType) {
159 return mimeType.type() == "video";
160 }
161 
162 template <IsMimeType T>
163 static constexpr bool isAudio(const T& mimeType) {
164 return mimeType.type() == "audio";
165 }
166 
167 static const MimeType PLAINTEXT;
168 static const MimeType PLAINTEXT_ASCII;
169 static constexpr ConstMimeType JSON = ConstMimeType("application"_kj, "json"_kj);
170 static constexpr ConstMimeType FORM_URLENCODED =
171 ConstMimeType("application"_kj, "x-www-form-urlencoded"_kj);
172 static constexpr ConstMimeType FORM_DATA = ConstMimeType("multipart"_kj, "form-data"_kj);
173 static constexpr ConstMimeType OCTET_STREAM = ConstMimeType("application"_kj, "octet-stream"_kj);
174 static constexpr ConstMimeType XHTML = ConstMimeType("application"_kj, "xhtml+xml"_kj);
175 static constexpr ConstMimeType JAVASCRIPT = ConstMimeType("application"_kj, "javascript"_kj);
176 static constexpr ConstMimeType XJAVASCRIPT = ConstMimeType("application"_kj, "x-javascript"_kj);
177 static constexpr ConstMimeType TEXT_JAVASCRIPT = ConstMimeType("text"_kj, "javascript"_kj);
178 static constexpr ConstMimeType HTML = ConstMimeType("text"_kj, "html"_kj);
179 static constexpr ConstMimeType CSS = ConstMimeType("text"_kj, "css"_kj);
180 static constexpr ConstMimeType MANIFEST_JSON =
181 ConstMimeType("application"_kj, "manifest+json"_kj);
182 static constexpr ConstMimeType VTT = ConstMimeType("text"_kj, "vtt"_kj);
183 static constexpr ConstMimeType EVENT_STREAM = ConstMimeType("text"_kj, "event-stream"_kj);
184 static constexpr ConstMimeType WILDCARD = ConstMimeType("*"_kj, "*"_kj);
185 
186 // exposed directly for performance reasons
187 static constexpr kj::StringPtr PLAINTEXT_STRING = "text/plain;charset=UTF-8"_kj;
188 static constexpr kj::StringPtr PLAINTEXT_ASCII_STRING = "text/plain;charset=US-ASCII"_kj;
189 
190 static kj::String formDataWithBoundary(kj::StringPtr boundary);
191 static kj::String formUrlEncodedWithCharset(kj::StringPtr charset);
192 
193 // Extracts a mime type from a concatenated list of content-type values
194 // per the algorithm defined in the fetch spec:
195 // https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
196 static kj::Maybe<MimeType> extract(kj::StringPtr input);
197 
198 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
199 tracker.trackFieldWithSize("type", type_.size());
200 tracker.trackFieldWithSize("subtype", subtype_.size());
201 tracker.trackFieldWithSize("params", params_.size());
202 }
203 
204 private:
205 kj::String type_;
206 kj::String subtype_;
207 MimeParams params_;
208 
209 using ToStringBuffer = StringBuffer<128>;
210 // 128 bytes will keep all reasonable mimetypes on the stack.
211 
212 void paramsToString(ToStringBuffer& buffer) const;
213};
214 
215inline ConstMimeType::operator MimeType() const {
216 return MimeType(type_, subtype_);
217}
218 
219inline MimeType ConstMimeType::clone() const {
220 return *this;
221}
222 
223inline bool ConstMimeType::operator==(const MimeType& other) const {
224 return type_ == other.type() && subtype_ == other.subtype();
225}
226 
227kj::String KJ_STRINGIFY(const MimeType& state);
228kj::String KJ_STRINGIFY(const ConstMimeType& state);
229 
230} // namespace workerd