1 #include "httprequest.h"
10 #define LOOPGUARD int nLoop = 0;
11 #define LOOPCHECK assert(nLoop++ < 10);
13 // HttpRequest --------------------------------------------------------------
15 HttpRequest::HttpRequest()
20 void HttpRequest::clear()
31 QByteArray HttpRequest::method() const
36 QByteArray HttpRequest::url() const
41 QByteArray HttpRequest::version() const
46 bool HttpRequest::has_attribute(QByteArray const &name) const
48 QByteArray name_ = name.toLower();
49 for(list_type::value_type pair : m_attributes)
50 if(name_ == pair.first.toLower()) return true;
54 QByteArray HttpRequest::attribute(QByteArray const &name) const
56 QByteArray name_ = name.toLower();
58 for(list_type::value_type pair : m_attributes)
59 if(name_ == pair.first.toLower()) result = pair.second;
63 HttpRequest::const_iterator HttpRequest::begin()
65 return m_attributes.begin();
68 HttpRequest::const_iterator HttpRequest::end()
70 return m_attributes.end();
73 QByteArray const& HttpRequest::content() const
78 void HttpRequest::state(int state)
83 int HttpRequest::state() const
88 int HttpRequest::content_length() const
90 if(!has_attribute("Content-Length")) return Error;
91 return attribute("Content-Length").toInt();
94 QByteArray HttpRequest::transfer_encoding() const
96 if(!has_attribute("Transfer-Encoding")) return "";
97 return attribute("Transfer-Encoding");
100 QByteArray HttpRequest::dump() const
102 QByteArray result = m_method + " " + url() + " " + version() + "\r\n";
103 for(list_type::value_type const &pair : m_attributes)
104 result += pair.first + ": " + pair.second + "\r\n";
105 result += "\r\n" + m_content;
108 // HttpRequestParser --------------------------------------------------------
110 HttpRequestParser::HttpRequestParser()
118 void HttpRequestParser::attach(HttpRequest *request)
121 assert(request->state() == HttpRequest::Initial);
128 void HttpRequestParser::detach(HttpRequest *request)
131 assert(m_request == request);
137 * \param offset 開始位置。
138 * \param bytes 入力となるバッファ。
141 * エラーがあった場合、m_requestにHttpRequest::Errorをセットします。
142 * 読み込みは途中で終わる場合があります。
145 int HttpRequestParser::push(int offset, QByteArray const &bytes)
152 if(m_state == Initial)
154 m_state = HeaderPartial;
155 m_request->state(HttpRequest::Partial);
158 if(m_state == HeaderPartial) nRead += parse(offset + nRead, bytes);
159 if(m_state == HeaderReady) m_state = ContentPartial;
161 if(m_state == ContentPartial) nRead += content(offset + nRead, bytes);
162 if(m_state == ContentReady) m_request->state(HttpRequest::Ready);
163 //if(m_state == Error) m_request->state(HttpRequest::ParseError);
169 int HttpRequestParser::parse(int offset, QByteArray const &bytes)
171 assert(m_state == HeaderPartial);
173 int size = bytes.length();
175 while(nRead + offset < size)
177 int result = parse(bytes.at(offset + nRead++));
180 qDebug() << "Error! on HttpRequestParser state:" << m_state0;
181 m_request->state(HttpRequest::ParseError);
186 if(m_state == HeaderReady) break;
192 * \brief HttpRequestParser::parse
194 * \return 成功=0、失敗=Error
196 * フィールド値以外の場所で暗黙的LWSには対応していません。
198 int HttpRequestParser::parse(char ch)
200 assert(m_state != Error && m_state0 != Error);
206 case 10: m_state0 = 10;
207 if(isToken(ch)) goto s11;
208 if(ch == ' ') goto s20;
210 case 11: s11: LOOPCHECK
211 m_request->m_method.append(ch);
214 case 20: s20: LOOPCHECK m_state0 = 21;
217 if(isURI(ch)) goto s22;
218 if(ch == ' ') goto s30;
220 case 22: s22: LOOPCHECK
221 m_request->m_url.append(ch);
224 case 30: s30: LOOPCHECK m_state0 = 31;
227 if(isUPALPHA(ch) || isDIGIT(ch) || ch == '.' || ch == '/'
228 || isLOALPHA(ch)) goto s32;
229 if(ch == '\r') goto s40;
231 case 32: s32: LOOPCHECK
232 m_request->m_version.append(ch);
235 case 40: s40: LOOPCHECK m_state0 = 41;
238 if(ch == '\n') goto s50;
241 case 50: s50: LOOPCHECK m_state0 = 110;
245 case 110: s110: LOOPCHECK m_state0 = 110;
246 if(isToken(ch)) goto s120;
247 if(ch == '\r') goto s130;
249 case 120: s120: LOOPCHECK m_state0 = 121;
250 m_request->m_attributes.append(HttpRequest::pair_type());
254 if(isToken(ch)) goto s122;
255 if(ch == '\r') goto s130;
256 if(ch == ':') goto s150;
258 case 122: s122: LOOPCHECK
259 m_request->m_attributes.last().first.append(ch);
262 case 130: s130: LOOPCHECK m_state0 = 131;
265 if(ch == '\n') goto s140;
267 case 140: s140: LOOPCHECK
268 m_state = HeaderReady; // リクエストヘッダ終了
271 case 150: s150: LOOPCHECK m_state0 = 210;
277 if(ch == ' ' || ch == '\t') goto s220;
278 if(ch == '\r') goto s260;
280 case 220: s220: LOOPCHECK m_state0 = 221;
283 if(ch == '\r') goto s260;
284 if(ch == ' ' || ch == '\t') break;
285 case 230: s230: LOOPCHECK m_state0 = 230;
286 if(ch == '"') goto s240;
287 if(ch == '\r') goto s260;
288 if(isCTL(ch)) goto sError;
289 m_request->m_attributes.last().second.append(ch);
292 case 240: s240: LOOPCHECK m_state0 = 241;
295 if(ch == '"') goto s230;
296 if(ch == '\\') goto s250;
297 if(isCTL(ch)) goto sError;
298 m_request->m_attributes.last().second.append(ch);
300 case 250: s250: LOOPCHECK this->m_state0 = 251;
303 if(!isCHAR(ch)) goto sError;
304 m_request->m_attributes.last().second.append(ch);
307 case 260: s260: LOOPCHECK this->m_state0 = 261;
310 if(ch != '\n') goto sError;
311 this->m_state0 = 270;
314 if(ch == ' ' || ch == '\t') goto s220;
315 if(ch == '\r') goto s280;
316 goto s110; // ヘッダフィールド名へ
317 case 280: s280: LOOPCHECK m_state0 = 281;
320 if(ch != '\n') goto sError;
321 m_state = HeaderReady; // リクエストヘッダ終了
325 case Error: sError: LOOPCHECK
331 int HttpRequestParser::content(int offset, QByteArray const &bytes)
335 if(m_request->has_attribute("content-length"))
337 int n = m_request->attribute("content-length").toInt();
338 m_cparser = ContentReader(n, this);
342 m_state = ContentReady;
346 return m_cparser(offset, bytes);
349 // HttpRequestParser::ContentReader ---------------------------------------
351 HttpRequestParser::ContentReader::ContentReader(int length, HttpRequestParser *parser)
358 int HttpRequestParser::ContentReader::operator()(int offset, QByteArray const &bytes)
360 int size = bytes.size();
362 for(; i < size; i++) m_parser->m_request->m_content.append(bytes.at(i));
363 m_length -= (i - offset);
364 if(!m_length) m_parser->m_state = HttpRequestParser::ContentReady;