Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(351)

Side by Side Diff: chrome/browser/chromeos/gdata/test_servers/http_request.cc

Issue 11088073: HTTP server for testing Google Drive. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Addressed comments. Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/gdata/test_servers/http_request.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <string>
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "googleurl/src/gurl.h"
13
14 namespace gdata {
15 namespace test_servers {
16
17 namespace {
18 int kRequestSizeLimit = 64 * 1024 * 1024; // 64 mb.
satorux1 2012/10/16 03:12:18 This server is used only for testing, so I think w
mtomasz 2012/11/08 13:29:59 Discussed offline, leaving as it is.
19 } // namespace
20
21 HttpRequestParser::HttpRequestParser() : request_builder_(new HttpRequest()),
22 state_(REQUEST_LINE),
23 buffer_position_(0),
24 crlf_position_(-1),
25 crlf_checked_position_(0),
26 current_content_length_(0) {
27 }
28
29 HttpRequestParser::~HttpRequestParser() {
30 }
31
32 void HttpRequestParser::ProcessChunk(const char *data, int length) {
33 buffer_.append(data, length);
34 if (static_cast<int>(buffer_.length()) + length > kRequestSizeLimit) {
35 // Too big request. Treat it as a syntax error.
36 LOG(ERROR) << "The HTTP request is too large.";
37 state_ = SYNTAX_ERROR;
38 return;
39 }
40 if (crlf_position_ == -1 && state_ != DATA)
41 FindNextCrLf();
satorux1 2012/10/16 06:39:48 I was confused about this. Why do we want to find
mtomasz 2012/11/08 13:29:59 You were right. Parser rewritten and simplified. D
42 }
43
44 HttpRequestParser::STATE HttpRequestParser::ParseRequest() {
satorux1 2012/10/16 03:12:18 The state machine code looks efficient, but for th
mtomasz 2012/11/08 13:29:59 Done. Please check the new parser.
45 while (state_ != READY &&
46 state_ != SYNTAX_ERROR &&
47 ShouldParseBuffer()) {
48 // StringPiece is converted to String, since a copy is needed in most cases
49 // to perform upper/lower case conversion.
50 std::string token;
51 switch (state_) {
52 case REQUEST_LINE:
53 // Parse a method, eg. GET.
54 token = ShiftToken(IDENTIFIER).as_string();
55 if (token.empty()) {
56 state_ = SYNTAX_ERROR;
57 break;
58 }
59 std::transform(token.begin(), token.end(), token.begin(), ::toupper);
satorux1 2012/10/16 06:39:48 StringToUpperASCII()
mtomasz 2012/11/08 13:29:59 Done.
60 if (token == "GET") {
61 request_builder_->method = GET;
62 } else if (token == "HEAD") {
63 request_builder_->method = HEAD;
64 } else if (token == "POST") {
65 request_builder_->method = POST;
66 } else if (token == "PUT") {
67 request_builder_->method = PUT;
68 } else if (token == "DELETE") {
69 request_builder_->method = DELETE;
70 } else {
71 // TODO(mtomasz): Implement other methods.
satorux1 2012/10/16 06:39:48 You can do NOTREACHED() << "Unsupported method: "
mtomasz 2012/11/08 13:29:59 Done.
72 request_builder_->method = CUSTOM;
73 }
satorux1 2012/10/16 06:39:48 Please create a function to convert string to meth
mtomasz 2012/11/08 13:29:59 Done.
74
75 // Parse an URL, eg. index.cgi.
76 token = ShiftToken(IDENTIFIER).as_string();
77 if (token.empty()) {
78 LOG(ERROR) << "The url token must not be empty.";
79 state_ = SYNTAX_ERROR;
80 break;
81 }
82 request_builder_->raw_url = token;
satorux1 2012/10/16 06:39:48 raw_url seems to be a misnomer. This is path + que
mtomasz 2012/11/08 13:29:59 Path_and_query looks to be quite long. How about j
83
84 // Parse a protocol, eg. HTTP/1.1.
85 // Currently we support only HTTP/1.1, since HTTP/1.0 is quite obsolete.
86 token = ShiftToken(LINE).as_string();
87 std::transform(token.begin(), token.end(), token.begin(), ::tolower);
88 if (token.empty() || token != "http/1.1") {
89 LOG(ERROR) << "This protocol is not supported: [" << token << "]";
90 state_ = SYNTAX_ERROR;
91 break;
92 }
93 request_builder_->protocol = token;
94 state_ = HEADER_LINE;
95 break;
96 case HEADER_LINE:
97 // If we have an empty line, then finish parsing headers and validate
98 // the request.
99 if (ShiftCrLf()) {
100 if (request_builder_->headers.find("host") ==
101 request_builder_->headers.end()) {
102 LOG(ERROR) << "Host header is required for HTTP/1.1";
103 state_ = SYNTAX_ERROR;
satorux1 2012/10/16 06:39:48 I think you don't have to check this. We can assum
mtomasz 2012/11/08 13:29:59 Done.
104 break;
105 } else {
106 // TODO(mtomasz): We add http:// protocol, but how about https://?
107 // Do we want to support it?
108 GURL host = GURL("http://" + request_builder_->headers["host"]);
satorux1 2012/10/16 06:39:48 Let's just use http://localhost for now
mtomasz 2012/11/08 13:29:59 Done.
109 request_builder_->url = host.Resolve(request_builder_->raw_url);
110 }
111 if (request_builder_->headers.find("content-length") !=
112 request_builder_->headers.end()) {
113 current_content_length_ =
114 atoi(request_builder_->headers["content-length"].c_str());
115 }
116 if (current_content_length_ > 0) {
117 state_ = DATA;
118 } else {
119 state_ = READY;
satorux1 2012/10/16 06:39:48 There is something called "chunked encoding" but w
mtomasz 2012/11/08 13:29:59 Done in the header file.
120 }
121 break;
122 }
123 {
124 // Parse a header name, eg. Content-length.
125 std::string header_name = ShiftToken(HEADER_KEY).as_string();
satorux1 2012/10/16 06:39:48 This assumes that |buffer_| contains the key name
mtomasz 2012/11/08 13:29:59 I don't think so. We were parsing headers line by
126 std::transform(header_name.begin(),
127 header_name.end(),
128 header_name.begin(),
129 ::tolower);
satorux1 2012/10/16 06:39:48 StringToLowerASCII
mtomasz 2012/11/08 13:29:59 Done.
130 if (header_name.empty()) {
131 LOG(ERROR) << "Header key must not be empty.";
132 state_ = SYNTAX_ERROR;
133 break;
134 }
135
136 // Parse a header value, eg. 100 (for Content-Length).
137 StringPiece header_value = ShiftToken(LINE);
satorux1 2012/10/16 06:39:48 I think the multi-line values are not supported ye
mtomasz 2012/11/08 13:29:59 Rewritten parser. Not it is supported. Done.
138 request_builder_->headers[header_name] = header_value.as_string();
139 break;
140 }
141 case DATA:
142 if (current_content_length_ ==
143 static_cast<int>(request_builder_->content.length())) {
144 // Entire data received.
145 state_ = READY;
146 } else {
147 int to_read = current_content_length_ -
148 request_builder_->content.length();
149 if (to_read > static_cast<int>(request_builder_->content.length()) -
150 buffer_position_) {
satorux1 2012/10/16 06:39:48 I'm confused. What does the following value mean?
mtomasz 2012/11/08 13:29:59 You were right. Removed. Done.
151 to_read = request_builder_->content.length() - buffer_position_;
152 }
153 StringPiece chunk = ShiftData(to_read);
154 DCHECK(chunk.length() > 0);
155 request_builder_->content.append(chunk.data(), chunk.length());
156 }
157 break;
158 case SYNTAX_ERROR:
159 break;
160 default:
161 state_ = SYNTAX_ERROR;
162 break;
163 }
164 }
165 return state_;
166 }
167
168 scoped_ptr<HttpRequest> HttpRequestParser::GetRequest() {
169 DCHECK(state_ == READY);
170 scoped_ptr<HttpRequest> result = request_builder_.Pass();
171 request_builder_.reset(new HttpRequest());
172
173 // Reset the parser state.
174 state_ = REQUEST_LINE;
175 buffer_position_ = 0;
176 buffer_.clear();
177 crlf_position_ = -1;
178 crlf_checked_position_ = 0;
179 current_content_length_ = 0;
satorux1 2012/10/16 06:39:48 Please add Reset() function that resets everything
mtomasz 2012/11/08 13:29:59 Do we need it? Now there is less stuff there. Also
180
181 return result.Pass();
182 }
183
184 const StringPiece HttpRequestParser::ShiftToken(SHIFT_TOKEN_TYPE token_type) {
185 SkipSpaces(); // Ignore any spaces from the beginning.
186 size_t found_position;
satorux1 2012/10/16 06:39:48 please remove this here.
mtomasz 2012/11/08 13:29:59 Done.
187 int token_start_position = buffer_position_;
188
189 switch (token_type) {
190 case IDENTIFIER:
191 found_position = buffer_.find(' ', buffer_position_);
satorux1 2012/10/16 06:39:48 const size_t found_position = ..
mtomasz 2012/11/08 13:29:59 Done.
192 if (found_position != std::string::npos) {
193 buffer_position_ = static_cast<int>(found_position) + 1;
194 return StringPiece(buffer_.data() + token_start_position,
195 found_position - token_start_position);
196 }
197 break;
198 case HEADER_KEY:
199 found_position = buffer_.find(':', buffer_position_);
200 if (found_position != std::string::npos) {
201 buffer_position_ = static_cast<int>(found_position) + 1;
202 return StringPiece(buffer_.data() + token_start_position,
203 found_position - token_start_position);
204 }
205 break;
206 case LINE:
207 DCHECK(crlf_position_ != -1) << "CRLF not found, but expected.";
208 buffer_position_ = crlf_position_ + 2;
209 int token_length = crlf_position_ - token_start_position;
satorux1 2012/10/16 06:39:48 I'm confused about this. Why don't we find the CRL
mtomasz 2012/11/08 13:29:59 This was to keep the parser's complexity O(n). Now
210 FindNextCrLf();
211 return StringPiece(buffer_.data() + token_start_position, token_length);
212 break;
213 }
214
215 return StringPiece();
216 }
217
218 bool HttpRequestParser::ShiftCrLf() {
219 if (buffer_position_ == crlf_position_) {
220 buffer_position_ += 2;
221 FindNextCrLf();
222 return true;
223 }
224
225 return false;
226 }
227
228 const StringPiece HttpRequestParser::ShiftData(int length) {
229 if (length > static_cast<int>(request_builder_->content.length()) -
230 buffer_position_) {
231 return StringPiece();
232 }
233
234 const StringPiece result(buffer_.data() + buffer_position_, length);
235 buffer_position_ += length;
236
237 return result;
238 }
239
240 void HttpRequestParser::SkipSpaces() {
241 StringPiece buffer_data(buffer_.data() + buffer_position_,
242 buffer_.length() - buffer_position_);
243
244 // Ignore any spaces from the beginning.
245 while (buffer_data.length() && buffer_data[0] == ' ') {
246 buffer_data.remove_prefix(1);
247 }
248 }
249
250 bool HttpRequestParser::ShouldParseBuffer() {
251 return buffer_position_ < static_cast<int>(buffer_.length()) &&
252 (crlf_position_ != -1 || state_ == DATA);
253 }
254
255 bool HttpRequestParser::FindNextCrLf() {
256 if (!buffer_.length()) {
257 return false;
258 }
259
260 size_t found_position = buffer_.find("\r\n", crlf_checked_position_);
261 if (found_position == std::string::npos) {
262 crlf_position_ = -1;
263 crlf_checked_position_ = buffer_.length() - 1;
264 return false;
265 }
266
267 crlf_position_ = static_cast<int>(found_position);
268 crlf_checked_position_ = crlf_position_ + 2;
269 return true;
270 }
271
272 } // namespace test_servers
273 } // namespace gdata
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698