comparison OrthancFramework/Sources/HttpServer/HttpContentNegociation.cpp @ 5338:78c59b02b121

accept parameters are now provided to HttpContentNegociation::IHandler::Handle()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 28 Jun 2023 08:29:43 +0200
parents 0ea402b4d901
children cb11e5ced4e3
comparison
equal deleted inserted replaced
5337:b376abae664a 5338:78c59b02b121
47 { 47 {
48 if (type == "*" && subtype == "*") 48 if (type == "*" && subtype == "*")
49 { 49 {
50 return true; 50 return true;
51 } 51 }
52 52 else if (subtype == "*" && type == type_)
53 if (subtype == "*" && type == type_)
54 { 53 {
55 return true; 54 return true;
56 } 55 }
57 56 else
58 return type == type_ && subtype == subtype_; 57 {
59 } 58 return type == type_ && subtype == subtype_;
60 59 }
61 60 }
62 struct HttpContentNegociation::Reference : public boost::noncopyable 61
63 { 62
63 class HttpContentNegociation::Reference : public boost::noncopyable
64 {
65 private:
64 const Handler& handler_; 66 const Handler& handler_;
65 uint8_t level_; 67 uint8_t level_;
66 float quality_; 68 float quality_;
67 69 std::string application_;
70 Dictionary parameters_;
71
72 static float GetQuality(const Dictionary& parameters)
73 {
74 Dictionary::const_iterator found = parameters.find("q");
75
76 if (found != parameters.end())
77 {
78 float quality;
79 bool ok = false;
80
81 try
82 {
83 quality = boost::lexical_cast<float>(found->second);
84 ok = (quality >= 0.0f && quality <= 1.0f);
85 }
86 catch (boost::bad_lexical_cast&)
87 {
88 }
89
90 if (ok)
91 {
92 return quality;
93 }
94 else
95 {
96 throw OrthancException(
97 ErrorCode_BadRequest,
98 "Quality parameter out of range in a HTTP request (must be between 0 and 1): " + found->second);
99 }
100 }
101 else
102 {
103 return 1.0f; // Default quality
104 }
105 }
106
107 public:
68 Reference(const Handler& handler, 108 Reference(const Handler& handler,
69 const std::string& type, 109 const std::string& type,
70 const std::string& subtype, 110 const std::string& subtype,
71 float quality) : 111 const Dictionary& parameters) :
72 handler_(handler), 112 handler_(handler),
73 quality_(quality) 113 quality_(GetQuality(parameters)),
114 application_(type + "/" + subtype),
115 parameters_(parameters)
74 { 116 {
75 if (type == "*" && subtype == "*") 117 if (type == "*" && subtype == "*")
76 { 118 {
77 level_ = 0; 119 level_ = 0;
78 } 120 }
82 } 124 }
83 else 125 else
84 { 126 {
85 level_ = 2; 127 level_ = 2;
86 } 128 }
129 }
130
131 void Call() const
132 {
133 handler_.Call(parameters_);
87 } 134 }
88 135
89 bool operator< (const Reference& other) const 136 bool operator< (const Reference& other) const
90 { 137 {
91 if (level_ < other.level_) 138 if (level_ < other.level_)
92 { 139 {
93 return true; 140 return true;
94 } 141 }
95 142 else if (level_ > other.level_)
96 if (level_ > other.level_)
97 { 143 {
98 return false; 144 return false;
99 } 145 }
100 146 else
101 return quality_ < other.quality_; 147 {
148 return quality_ < other.quality_;
149 }
102 } 150 }
103 }; 151 };
104 152
105 153
106 bool HttpContentNegociation::SplitPair(std::string& first /* out */, 154 bool HttpContentNegociation::SplitPair(std::string& first /* out */,
121 return true; 169 return true;
122 } 170 }
123 } 171 }
124 172
125 173
126 float HttpContentNegociation::GetQuality(const Tokens& parameters) 174 void HttpContentNegociation::SelectBestMatch(std::unique_ptr<Reference>& target,
127 {
128 for (size_t i = 1; i < parameters.size(); i++)
129 {
130 std::string key, value;
131 if (SplitPair(key, value, parameters[i], '=') &&
132 key == "q")
133 {
134 float quality;
135 bool ok = false;
136
137 try
138 {
139 quality = boost::lexical_cast<float>(value);
140 ok = (quality >= 0.0f && quality <= 1.0f);
141 }
142 catch (boost::bad_lexical_cast&)
143 {
144 }
145
146 if (ok)
147 {
148 return quality;
149 }
150 else
151 {
152 throw OrthancException(
153 ErrorCode_BadRequest,
154 "Quality parameter out of range in a HTTP request (must be between 0 and 1): " + value);
155 }
156 }
157 }
158
159 return 1.0f; // Default quality
160 }
161
162
163 void HttpContentNegociation::SelectBestMatch(std::unique_ptr<Reference>& best,
164 const Handler& handler, 175 const Handler& handler,
165 const std::string& type, 176 const std::string& type,
166 const std::string& subtype, 177 const std::string& subtype,
167 float quality) 178 const Dictionary& parameters)
168 { 179 {
169 std::unique_ptr<Reference> match(new Reference(handler, type, subtype, quality)); 180 std::unique_ptr<Reference> match(new Reference(handler, type, subtype, parameters));
170 181
171 if (best.get() == NULL || 182 if (target.get() == NULL ||
172 *best < *match) 183 *target < *match)
173 { 184 {
174 #if __cplusplus < 201103L 185 #if __cplusplus < 201103L
175 best.reset(match.release()); 186 target.reset(match.release());
176 #else 187 #else
177 best = std::move(match); 188 target = std::move(match);
178 #endif 189 #endif
179 } 190 }
180 } 191 }
181 192
182 193
196 throw OrthancException(ErrorCode_ParameterOutOfRange); 207 throw OrthancException(ErrorCode_ParameterOutOfRange);
197 } 208 }
198 } 209 }
199 210
200 211
201 bool HttpContentNegociation::Apply(const HttpHeaders& headers) 212 bool HttpContentNegociation::Apply(const Dictionary& headers)
202 { 213 {
203 HttpHeaders::const_iterator accept = headers.find("accept"); 214 Dictionary::const_iterator accept = headers.find("accept");
204 if (accept != headers.end()) 215 if (accept != headers.end())
205 { 216 {
206 return Apply(accept->second); 217 return Apply(accept->second);
207 } 218 }
208 else 219 else
220 231
221 Tokens mediaRanges; 232 Tokens mediaRanges;
222 Toolbox::TokenizeString(mediaRanges, accept, ','); 233 Toolbox::TokenizeString(mediaRanges, accept, ',');
223 234
224 std::unique_ptr<Reference> bestMatch; 235 std::unique_ptr<Reference> bestMatch;
236 Dictionary bestParameters;
225 237
226 for (Tokens::const_iterator it = mediaRanges.begin(); 238 for (Tokens::const_iterator it = mediaRanges.begin();
227 it != mediaRanges.end(); ++it) 239 it != mediaRanges.end(); ++it)
228 { 240 {
229 Tokens parameters; 241 Tokens tokens;
230 Toolbox::TokenizeString(parameters, *it, ';'); 242 Toolbox::TokenizeString(tokens, *it, ';');
231 243
232 if (parameters.size() > 0) 244 if (tokens.size() > 0)
233 { 245 {
234 float quality = GetQuality(parameters); 246 Dictionary parameters;
235 247 for (size_t i = 1; i < tokens.size(); i++)
248 {
249 std::string key, value;
250
251 if (!SplitPair(key, value, tokens[i], '='))
252 {
253 key = Toolbox::StripSpaces(tokens[i]);
254 value = "";
255 }
256
257 parameters[key] = value;
258 }
259
236 std::string type, subtype; 260 std::string type, subtype;
237 if (SplitPair(type, subtype, parameters[0], '/')) 261 if (SplitPair(type, subtype, tokens[0], '/'))
238 { 262 {
239 for (Handlers::const_iterator it2 = handlers_.begin(); 263 for (Handlers::const_iterator it2 = handlers_.begin();
240 it2 != handlers_.end(); ++it2) 264 it2 != handlers_.end(); ++it2)
241 { 265 {
242 if (it2->IsMatch(type, subtype)) 266 if (it2->IsMatch(type, subtype))
243 { 267 {
244 SelectBestMatch(bestMatch, *it2, type, subtype, quality); 268 SelectBestMatch(bestMatch, *it2, type, subtype, parameters);
245 } 269 }
246 } 270 }
247 } 271 }
248 } 272 }
249 } 273 }
252 { 276 {
253 return false; 277 return false;
254 } 278 }
255 else 279 else
256 { 280 {
257 bestMatch->handler_.Call(); 281 bestMatch->Call();
258 return true; 282 return true;
259 } 283 }
260 } 284 }
261 } 285 }