OLD | NEW |
1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // This file implements a simple HTTP server. It can exhibit odd behavior | 5 // This file implements a simple HTTP server. It can exhibit odd behavior |
6 // that's useful for testing. For example, it's useful to test that | 6 // that's useful for testing. For example, it's useful to test that |
7 // the updater can continue a connection if it's dropped, or that it | 7 // the updater can continue a connection if it's dropped, or that it |
8 // handles very slow data transfers. | 8 // handles very slow data transfers. |
9 | 9 |
10 // To use this, simply make an HTTP connection to localhost:port and | 10 // To use this, simply make an HTTP connection to localhost:port and |
(...skipping 14 matching lines...) Expand all Loading... |
25 #include "base/logging.h" | 25 #include "base/logging.h" |
26 | 26 |
27 using std::min; | 27 using std::min; |
28 using std::string; | 28 using std::string; |
29 using std::vector; | 29 using std::vector; |
30 | 30 |
31 namespace chromeos_update_engine { | 31 namespace chromeos_update_engine { |
32 | 32 |
33 struct HttpRequest { | 33 struct HttpRequest { |
34 HttpRequest() : offset(0), return_code(200) {} | 34 HttpRequest() : offset(0), return_code(200) {} |
| 35 string host; |
35 string url; | 36 string url; |
36 off_t offset; | 37 off_t offset; |
37 int return_code; | 38 int return_code; |
38 }; | 39 }; |
39 | 40 |
40 namespace { | 41 namespace { |
41 const int kPort = 8080; // hardcoded to 8080 for now | 42 const int kPort = 8080; // hardcoded to 8080 for now |
42 const int kBigLength = 100000; | 43 const int kBigLength = 100000; |
| 44 const int kMediumLength = 1000; |
43 } | 45 } |
44 | 46 |
45 bool ParseRequest(int fd, HttpRequest* request) { | 47 bool ParseRequest(int fd, HttpRequest* request) { |
46 string headers; | 48 string headers; |
47 while(headers.find("\r\n\r\n") == string::npos) { | 49 while(headers.find("\r\n\r\n") == string::npos) { |
48 vector<char> buf(1024); | 50 vector<char> buf(1024); |
49 memset(&buf[0], 0, buf.size()); | 51 memset(&buf[0], 0, buf.size()); |
50 ssize_t r = read(fd, &buf[0], buf.size() - 1); | 52 ssize_t r = read(fd, &buf[0], buf.size() - 1); |
51 if (r < 0) { | 53 if (r < 0) { |
52 perror("read"); | 54 perror("read"); |
53 exit(1); | 55 exit(1); |
54 } | 56 } |
55 buf.resize(r); | 57 buf.resize(r); |
56 | 58 |
57 headers.insert(headers.end(), buf.begin(), buf.end()); | 59 headers.insert(headers.end(), buf.begin(), buf.end()); |
58 } | 60 } |
59 LOG(INFO) << "got headers: " << headers; | 61 LOG(INFO) << "got headers: " << headers; |
60 | 62 |
61 string::size_type url_start, url_end; | 63 string::size_type url_start, url_end; |
62 CHECK_NE(headers.find("GET "), string::npos); | 64 CHECK_NE(headers.find("GET "), string::npos); |
63 url_start = headers.find("GET ") + strlen("GET "); | 65 url_start = headers.find("GET ") + strlen("GET "); |
64 url_end = headers.find(' ', url_start); | 66 url_end = headers.find(' ', url_start); |
65 CHECK_NE(string::npos, url_end); | 67 CHECK_NE(string::npos, url_end); |
66 string url = headers.substr(url_start, url_end - url_start); | 68 string url = headers.substr(url_start, url_end - url_start); |
67 LOG(INFO) << "URL: " << url; | 69 LOG(INFO) << "URL: " << url; |
| 70 request->url = url; |
68 | 71 |
69 string::size_type range_start, range_end; | 72 string::size_type range_start, range_end; |
70 if (headers.find("\r\nRange: ") == string::npos) { | 73 if (headers.find("\r\nRange: ") == string::npos) { |
71 request->offset = 0; | 74 request->offset = 0; |
72 } else { | 75 } else { |
73 range_start = headers.find("\r\nRange: ") + strlen("\r\nRange: "); | 76 range_start = headers.find("\r\nRange: ") + strlen("\r\nRange: "); |
74 range_end = headers.find('\r', range_start); | 77 range_end = headers.find('\r', range_start); |
75 CHECK_NE(string::npos, range_end); | 78 CHECK_NE(string::npos, range_end); |
76 string range_header = headers.substr(range_start, range_end - range_start); | 79 string range_header = headers.substr(range_start, range_end - range_start); |
77 | 80 |
78 LOG(INFO) << "Range: " << range_header; | 81 LOG(INFO) << "Range: " << range_header; |
79 CHECK(*range_header.rbegin() == '-'); | 82 CHECK(*range_header.rbegin() == '-'); |
80 request->offset = atoll(range_header.c_str() + strlen("bytes=")); | 83 request->offset = atoll(range_header.c_str() + strlen("bytes=")); |
81 request->return_code = 206; // Success for Range: request | 84 request->return_code = 206; // Success for Range: request |
82 LOG(INFO) << "Offset: " << request->offset; | 85 LOG(INFO) << "Offset: " << request->offset; |
83 } | 86 } |
84 request->url = url; | 87 |
| 88 if (headers.find("\r\nHost: ") == string::npos) { |
| 89 request->host = ""; |
| 90 } else { |
| 91 string::size_type host_start = |
| 92 headers.find("\r\nHost: ") + strlen("\r\nHost: "); |
| 93 string::size_type host_end = headers.find('\r', host_start); |
| 94 CHECK_NE(string::npos, host_end); |
| 95 string host = headers.substr(host_start, host_end - host_start); |
| 96 |
| 97 LOG(INFO) << "Host: " << host; |
| 98 request->host = host; |
| 99 } |
| 100 |
85 return true; | 101 return true; |
86 } | 102 } |
87 | 103 |
88 void WriteString(int fd, const string& str) { | 104 void WriteString(int fd, const string& str) { |
89 unsigned int bytes_written = 0; | 105 unsigned int bytes_written = 0; |
90 while (bytes_written < str.size()) { | 106 while (bytes_written < str.size()) { |
91 ssize_t r = write(fd, str.c_str() + bytes_written, | 107 ssize_t r = write(fd, str.c_str() + bytes_written, |
92 str.size() - bytes_written); | 108 str.size() - bytes_written); |
93 LOG(INFO) << "write() wrote " << r << " bytes"; | 109 LOG(INFO) << "write() wrote " << r << " bytes"; |
94 if (r < 0) { | 110 if (r < 0) { |
(...skipping 26 matching lines...) Expand all Loading... |
121 content_length -= start_offset; | 137 content_length -= start_offset; |
122 WriteString(fd, string("Content-Length: ") + Itoa(content_length) + "\r\n"); | 138 WriteString(fd, string("Content-Length: ") + Itoa(content_length) + "\r\n"); |
123 WriteString(fd, "\r\n"); | 139 WriteString(fd, "\r\n"); |
124 } | 140 } |
125 | 141 |
126 void HandleQuitQuitQuit(int fd) { | 142 void HandleQuitQuitQuit(int fd) { |
127 WriteHeaders(fd, true, 0, 0, 200); | 143 WriteHeaders(fd, true, 0, 0, 200); |
128 exit(0); | 144 exit(0); |
129 } | 145 } |
130 | 146 |
131 void HandleBig(int fd, const HttpRequest& request) { | 147 void HandleBig(int fd, const HttpRequest& request, int big_length) { |
132 const off_t full_length = kBigLength; | 148 const off_t full_length = big_length; |
133 WriteHeaders(fd, true, full_length, request.offset, request.return_code); | 149 WriteHeaders(fd, true, full_length, request.offset, request.return_code); |
134 const off_t content_length = full_length - request.offset; | 150 const off_t content_length = full_length - request.offset; |
135 int i = request.offset; | 151 int i = request.offset; |
136 for (; i % 10; i++) | 152 for (; i % 10; i++) |
137 WriteString(fd, string(1, 'a' + (i % 10))); | 153 WriteString(fd, string(1, 'a' + (i % 10))); |
138 CHECK_EQ(i % 10, 0); | 154 CHECK_EQ(i % 10, 0); |
139 for (; i < content_length; i += 10) | 155 for (; i < content_length; i += 10) |
140 WriteString(fd, "abcdefghij"); | 156 WriteString(fd, "abcdefghij"); |
141 CHECK_EQ(i, full_length); | 157 CHECK_EQ(i, full_length); |
142 } | 158 } |
(...skipping 23 matching lines...) Expand all Loading... |
166 string::size_type half_way_point = buf.size() / 2; | 182 string::size_type half_way_point = buf.size() / 2; |
167 LOG(INFO) << "writing small data blob of size " << half_way_point; | 183 LOG(INFO) << "writing small data blob of size " << half_way_point; |
168 WriteString(fd, buf.substr(0, half_way_point)); | 184 WriteString(fd, buf.substr(0, half_way_point)); |
169 sleep(10); | 185 sleep(10); |
170 LOG(INFO) << "writing small data blob of size " | 186 LOG(INFO) << "writing small data blob of size " |
171 << (buf.size() - half_way_point); | 187 << (buf.size() - half_way_point); |
172 WriteString(fd, buf.substr(half_way_point, buf.size() - half_way_point)); | 188 WriteString(fd, buf.substr(half_way_point, buf.size() - half_way_point)); |
173 } | 189 } |
174 } | 190 } |
175 | 191 |
| 192 // Handles /redirect/<code>/<url> requests by returning the specified |
| 193 // redirect <code> with a location pointing to /<url>. |
| 194 void HandleRedirect(int fd, const HttpRequest& request) { |
| 195 LOG(INFO) << "Redirecting..."; |
| 196 string url = request.url; |
| 197 CHECK_EQ(0, url.find("/redirect/")); |
| 198 url.erase(0, strlen("/redirect/")); |
| 199 string::size_type url_start = url.find('/'); |
| 200 CHECK_NE(url_start, string::npos); |
| 201 string code = url.substr(0, url_start); |
| 202 url.erase(0, url_start); |
| 203 url = "http://" + request.host + url; |
| 204 string status; |
| 205 if (code == "301") { |
| 206 status = "Moved Permanently"; |
| 207 } else if (code == "302") { |
| 208 status = "Found"; |
| 209 } else if (code == "303") { |
| 210 status = "See Other"; |
| 211 } else if (code == "307") { |
| 212 status = "Temporary Redirect"; |
| 213 } else { |
| 214 CHECK(false) << "Unrecognized redirection code: " << code; |
| 215 } |
| 216 LOG(INFO) << "Code: " << code << " " << status; |
| 217 LOG(INFO) << "New URL: " << url; |
| 218 WriteString(fd, "HTTP/1.1 " + code + " " + status + "\r\n"); |
| 219 WriteString(fd, "Location: " + url + "\r\n"); |
| 220 } |
| 221 |
176 void HandleDefault(int fd, const HttpRequest& request) { | 222 void HandleDefault(int fd, const HttpRequest& request) { |
177 const string data("unhandled path"); | 223 const string data("unhandled path"); |
178 WriteHeaders(fd, true, data.size(), request.offset, request.return_code); | 224 WriteHeaders(fd, true, data.size(), request.offset, request.return_code); |
179 const string data_to_write(data.substr(request.offset, | 225 const string data_to_write(data.substr(request.offset, |
180 data.size() - request.offset)); | 226 data.size() - request.offset)); |
181 WriteString(fd, data_to_write); | 227 WriteString(fd, data_to_write); |
182 } | 228 } |
183 | 229 |
184 void HandleConnection(int fd) { | 230 void HandleConnection(int fd) { |
185 HttpRequest request; | 231 HttpRequest request; |
186 ParseRequest(fd, &request); | 232 ParseRequest(fd, &request); |
187 | 233 |
188 if (request.url == "/quitquitquit") | 234 if (request.url == "/quitquitquit") |
189 HandleQuitQuitQuit(fd); | 235 HandleQuitQuitQuit(fd); |
190 else if (request.url == "/big") | 236 else if (request.url == "/big") |
191 HandleBig(fd, request); | 237 HandleBig(fd, request, kBigLength); |
| 238 else if (request.url == "/medium") |
| 239 HandleBig(fd, request, kMediumLength); |
192 else if (request.url == "/flaky") | 240 else if (request.url == "/flaky") |
193 HandleFlaky(fd, request); | 241 HandleFlaky(fd, request); |
| 242 else if (request.url.find("/redirect/") == 0) |
| 243 HandleRedirect(fd, request); |
194 else | 244 else |
195 HandleDefault(fd, request); | 245 HandleDefault(fd, request); |
196 | 246 |
197 close(fd); | 247 close(fd); |
198 } | 248 } |
199 | 249 |
200 } // namespace chromeos_update_engine | 250 } // namespace chromeos_update_engine |
201 | 251 |
202 using namespace chromeos_update_engine; | 252 using namespace chromeos_update_engine; |
203 | 253 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 int client_fd = accept(listen_fd, | 287 int client_fd = accept(listen_fd, |
238 (struct sockaddr *) &client_addr, | 288 (struct sockaddr *) &client_addr, |
239 &clilen); | 289 &clilen); |
240 LOG(INFO) << "got past accept"; | 290 LOG(INFO) << "got past accept"; |
241 if (client_fd < 0) | 291 if (client_fd < 0) |
242 LOG(FATAL) << "ERROR on accept"; | 292 LOG(FATAL) << "ERROR on accept"; |
243 HandleConnection(client_fd); | 293 HandleConnection(client_fd); |
244 } | 294 } |
245 return 0; | 295 return 0; |
246 } | 296 } |
OLD | NEW |