OSDN Git Service

Initial commit
[wordring-tm/wordring-tm.git] / http / httprequest.cpp
1 #include "httprequest.h"
2
3 #include <QString>
4 #include <QByteArray>
5
6 #include <QTcpSocket>
7
8 #include "debug.h"
9
10 #define LOOPGUARD int nLoop = 0;
11 #define LOOPCHECK assert(nLoop++ < 10);
12
13 // HttpRequest --------------------------------------------------------------
14
15 HttpRequest::HttpRequest()
16         : m_state(Initial)
17 {
18 }
19
20 void HttpRequest::clear()
21 {
22         m_state = Initial;
23
24         m_method.clear();
25         m_url.clear();
26         m_version.clear();
27         m_attributes.clear();
28         m_content.clear();
29 }
30
31 QByteArray HttpRequest::method() const
32 {
33         return m_method;
34 }
35
36 QByteArray HttpRequest::url() const
37 {
38         return m_url;
39 }
40
41 QByteArray HttpRequest::version() const
42 {
43         return m_version;
44 }
45
46 bool HttpRequest::has_attribute(QByteArray const &name) const
47 {
48         QByteArray name_ = name.toLower();
49         for(list_type::value_type pair : m_attributes)
50                 if(name_ == pair.first.toLower()) return true;
51         return false;
52 }
53
54 QByteArray HttpRequest::attribute(QByteArray const &name) const
55 {
56         QByteArray name_ = name.toLower();
57         QByteArray result;
58         for(list_type::value_type pair : m_attributes)
59                 if(name_ == pair.first.toLower()) result = pair.second;
60         return result;
61 }
62
63 HttpRequest::const_iterator HttpRequest::begin()
64 {
65         return m_attributes.begin();
66 }
67
68 HttpRequest::const_iterator HttpRequest::end()
69 {
70         return m_attributes.end();
71 }
72
73 QByteArray const& HttpRequest::content() const
74 {
75         return m_content;
76 }
77
78 void HttpRequest::state(int state)
79 {
80         m_state = state;
81 }
82
83 int HttpRequest::state() const
84 {
85         return m_state;
86 }
87
88 int HttpRequest::content_length() const
89 {
90         if(!has_attribute("Content-Length")) return Error;
91         return attribute("Content-Length").toInt();
92 }
93
94 QByteArray HttpRequest::transfer_encoding() const
95 {
96         if(!has_attribute("Transfer-Encoding")) return "";
97         return attribute("Transfer-Encoding");
98 }
99
100 QByteArray HttpRequest::dump() const
101 {
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;
106         return result;
107 }
108 // HttpRequestParser --------------------------------------------------------
109
110 HttpRequestParser::HttpRequestParser()
111         : m_request(nullptr)
112         , m_state(Initial)
113         , m_state0(0)
114 {
115
116 }
117
118 void HttpRequestParser::attach(HttpRequest *request)
119 {
120         assert(!m_request);
121         assert(request->state() == HttpRequest::Initial);
122         m_request = request;
123         m_state = Initial;
124         m_state0 = 10;
125         m_cparser = nullptr;
126 }
127
128 void HttpRequestParser::detach(HttpRequest *request)
129 {
130         request;
131         assert(m_request == request);
132         m_request = nullptr;
133 }
134
135 /*!
136  * \brief 入力します。
137  * \param offset 開始位置。
138  * \param bytes 入力となるバッファ。
139  * \return 読み込んだバイト数。
140  *
141  * エラーがあった場合、m_requestにHttpRequest::Errorをセットします。
142  * 読み込みは途中で終わる場合があります。
143  * 戻り値を必ずチェックしてください。
144  */
145 int HttpRequestParser::push(int offset, QByteArray const &bytes)
146 {
147         assert(m_request);
148
149         int nRead = 0;
150
151         // パーサ初期化
152         if(m_state == Initial)
153         {
154                 m_state = HeaderPartial;
155                 m_request->state(HttpRequest::Partial);
156         }
157         // ヘッダ解析
158         if(m_state == HeaderPartial) nRead += parse(offset + nRead, bytes);
159         if(m_state == HeaderReady) m_state = ContentPartial;
160         // コンテント解析
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);
164
165         m_count += nRead;
166         return nRead;
167 }
168
169 int HttpRequestParser::parse(int offset, QByteArray const &bytes)
170 {
171         assert(m_state == HeaderPartial);
172
173         int size = bytes.length();
174         int nRead = 0;
175         while(nRead + offset < size)
176         {
177                 int result = parse(bytes.at(offset + nRead++));
178                 if(result == Error)
179                 {
180                         qDebug() << "Error! on HttpRequestParser state:" << m_state0;
181                         m_request->state(HttpRequest::ParseError);
182                         m_state = Error;
183                         m_state0 = Error;
184                         break;
185                 }
186                 if(m_state == HeaderReady) break;
187         }
188         return nRead;
189 }
190
191 /*!
192  * \brief HttpRequestParser::parse
193  * \param ch
194  * \return 成功=0、失敗=Error
195  *
196  * フィールド値以外の場所で暗黙的LWSには対応していません。
197  */
198 int HttpRequestParser::parse(char ch)
199 {
200         assert(m_state != Error && m_state0 != Error);
201         LOOPGUARD
202         switch(m_state0)
203         {
204 // リクエストライン
205         // メソッド
206         case 10: m_state0 = 10;
207                 if(isToken(ch)) goto s11;
208                 if(ch == ' ') goto s20;
209                 goto sError;
210         case 11: s11: LOOPCHECK
211                 m_request->m_method.append(ch);
212                 break;
213         // URI
214         case 20: s20: LOOPCHECK m_state0 = 21;
215                 break;
216         case 21:
217                 if(isURI(ch)) goto s22;
218                 if(ch == ' ') goto s30;
219                 goto sError;
220         case 22: s22: LOOPCHECK
221                 m_request->m_url.append(ch);
222                 break;
223         // バージョン
224         case 30: s30: LOOPCHECK m_state0 = 31;
225                 break;
226         case 31:
227                 if(isUPALPHA(ch) || isDIGIT(ch) || ch == '.' || ch == '/'
228                                 || isLOALPHA(ch)) goto s32;
229                 if(ch == '\r') goto s40;
230                 goto sError;
231         case 32: s32: LOOPCHECK
232                 m_request->m_version.append(ch);
233                 break;
234         // CRLF
235         case 40: s40: LOOPCHECK m_state0 = 41;
236                 break;
237         case 41:
238                 if(ch == '\n') goto s50;
239                 goto sError;
240         // リクエストライン終了
241         case 50: s50: LOOPCHECK m_state0 = 110;
242                 break;
243
244 // フィールド名
245         case 110: s110: LOOPCHECK m_state0 = 110;
246                 if(isToken(ch)) goto s120;
247                 if(ch == '\r') goto s130;
248                 goto sError;
249         case 120: s120: LOOPCHECK m_state0 = 121;
250                 m_request->m_attributes.append(HttpRequest::pair_type());
251                 goto s122;
252         // トークン
253         case 121:
254                 if(isToken(ch)) goto s122;
255                 if(ch == '\r') goto s130;
256                 if(ch == ':') goto s150;
257                 goto sError;
258         case 122: s122: LOOPCHECK
259                 m_request->m_attributes.last().first.append(ch);
260                 break;
261         // CRLF
262         case 130: s130: LOOPCHECK m_state0 = 131;
263                 break;
264         case 131:
265                 if(ch == '\n') goto s140;
266                 goto sError;
267         case 140: s140: LOOPCHECK
268                 m_state = HeaderReady; // リクエストヘッダ終了
269                 break;
270         // フィールド名終了
271         case 150: s150: LOOPCHECK m_state0 = 210;
272                 break;
273
274 // フィールド値
275         // LWS
276         case 210:
277                 if(ch == ' ' || ch == '\t') goto s220;
278                 if(ch == '\r') goto s260;
279                 goto sError;
280         case 220: s220: LOOPCHECK m_state0 = 221;
281                 break;
282         case 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);
290                 break;
291         // クォート文字列
292         case 240: s240: LOOPCHECK m_state0 = 241;
293                 break;
294         case 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);
299                 break;
300         case 250: s250: LOOPCHECK this->m_state0 = 251;
301                 break;
302         case 251:
303                 if(!isCHAR(ch)) goto sError;
304                 m_request->m_attributes.last().second.append(ch);
305                 m_state0 = 241;
306                 break;
307         case 260: s260: LOOPCHECK this->m_state0 = 261;
308                 break;
309         case 261:
310                 if(ch != '\n') goto sError;
311                 this->m_state0 = 270;
312                 break;
313         case 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;
318                 break;
319         case 281:
320                 if(ch != '\n') goto sError;
321                 m_state = HeaderReady; // リクエストヘッダ終了
322                 break;
323
324 // エラー
325         case Error: sError: LOOPCHECK
326                 return Error;
327         }
328         return 0;
329 }
330
331 int HttpRequestParser::content(int offset, QByteArray const &bytes)
332 {
333         if(!m_cparser)
334         {
335                 if(m_request->has_attribute("content-length"))
336                 {
337                         int n = m_request->attribute("content-length").toInt();
338                         m_cparser = ContentReader(n, this);
339                 }
340                 else
341                 {
342                         m_state = ContentReady;
343                         return 0;
344                 }
345         }
346         return m_cparser(offset, bytes);
347 }
348
349 // HttpRequestParser::ContentReader ---------------------------------------
350
351 HttpRequestParser::ContentReader::ContentReader(int length, HttpRequestParser *parser)
352         : m_length(length)
353         , m_parser(parser)
354 {
355
356 }
357
358 int HttpRequestParser::ContentReader::operator()(int offset, QByteArray const &bytes)
359 {
360         int size = bytes.size();
361         int i = offset;
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;
365         return i - offset;
366 }
367
368
369
370
371
372
373
374