blob: 2272052863a67302808041abb7ecf85b8a08fc72 [file] [log] [blame]
Mike Frysinger8155d082012-04-06 15:23:18 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
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
7// the updater can continue a connection if it's dropped, or that it
8// handles very slow data transfers.
9
10// To use this, simply make an HTTP connection to localhost:port and
11// GET a url.
12
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070013#include <err.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000014#include <errno.h>
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070015#include <inttypes.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070016#include <netinet/in.h>
17#include <signal.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000018#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070021#include <sys/socket.h>
22#include <sys/types.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000023#include <unistd.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070024
adlr@google.com3defe6a2009-12-04 20:57:17 +000025#include <algorithm>
26#include <string>
27#include <vector>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070028
29#include <base/logging.h>
Gilad Arnold9bedeb52011-11-17 16:19:57 -080030#include <base/string_split.h>
31#include <base/string_util.h>
Mike Frysinger8155d082012-04-06 15:23:18 -040032#include <base/stringprintf.h>
Gilad Arnold9bedeb52011-11-17 16:19:57 -080033
34#include "update_engine/http_common.h"
Gilad Arnold9bedeb52011-11-17 16:19:57 -080035
adlr@google.com3defe6a2009-12-04 20:57:17 +000036
Gilad Arnolde4ad2502011-12-29 17:08:54 -080037// HTTP end-of-line delimiter; sorry, this needs to be a macro.
38#define EOL "\r\n"
39
adlr@google.com3defe6a2009-12-04 20:57:17 +000040using std::min;
41using std::string;
42using std::vector;
43
Gilad Arnold9bedeb52011-11-17 16:19:57 -080044
adlr@google.com3defe6a2009-12-04 20:57:17 +000045namespace chromeos_update_engine {
46
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070047// Allowed port range and default value.
48const long kPortMin = static_cast<long>(1) << 10;
49const long kPortMax = (static_cast<long>(1) << 16) - 1;
50const in_port_t kPortDefault = 8080;
51
52enum {
53 RC_OK = 0,
54 RC_BAD_ARGS,
55 RC_ERR_READ,
56 RC_ERR_SETSOCKOPT,
57 RC_ERR_BIND,
58};
59
adlr@google.com3defe6a2009-12-04 20:57:17 +000060struct HttpRequest {
Gilad Arnolde4ad2502011-12-29 17:08:54 -080061 HttpRequest()
62 : start_offset(0), end_offset(0), return_code(kHttpResponseOk) {}
Darin Petkov41c2fcf2010-08-25 13:14:48 -070063 string host;
adlr@google.com3defe6a2009-12-04 20:57:17 +000064 string url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -080065 off_t start_offset;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080066 off_t end_offset; // non-inclusive, zero indicates unspecified.
Gilad Arnold9bedeb52011-11-17 16:19:57 -080067 HttpResponseCode return_code;
adlr@google.com3defe6a2009-12-04 20:57:17 +000068};
69
adlr@google.com3defe6a2009-12-04 20:57:17 +000070bool ParseRequest(int fd, HttpRequest* request) {
71 string headers;
Gilad Arnold9bedeb52011-11-17 16:19:57 -080072 do {
73 char buf[1024];
74 ssize_t r = read(fd, buf, sizeof(buf));
adlr@google.com3defe6a2009-12-04 20:57:17 +000075 if (r < 0) {
76 perror("read");
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070077 exit(RC_ERR_READ);
adlr@google.com3defe6a2009-12-04 20:57:17 +000078 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -080079 headers.append(buf, r);
80 } while (!EndsWith(headers, EOL EOL, true));
adlr@google.com3defe6a2009-12-04 20:57:17 +000081
Gilad Arnold9bedeb52011-11-17 16:19:57 -080082 LOG(INFO) << "got headers:\n--8<------8<------8<------8<----\n"
83 << headers
84 << "\n--8<------8<------8<------8<----";
adlr@google.com3defe6a2009-12-04 20:57:17 +000085
Gilad Arnold9bedeb52011-11-17 16:19:57 -080086 // Break header into lines.
87 std::vector<string> lines;
88 base::SplitStringUsingSubstr(
89 headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
adlr@google.com3defe6a2009-12-04 20:57:17 +000090
Gilad Arnold9bedeb52011-11-17 16:19:57 -080091 // Decode URL line.
92 std::vector<string> terms;
93 base::SplitStringAlongWhitespace(lines[0], &terms);
Mike Frysinger0f9547d2012-02-16 12:11:37 -050094 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(3));
Gilad Arnold9bedeb52011-11-17 16:19:57 -080095 CHECK_EQ(terms[0], "GET");
96 request->url = terms[1];
97 LOG(INFO) << "URL: " << request->url;
adlr@google.com3defe6a2009-12-04 20:57:17 +000098
Gilad Arnold9bedeb52011-11-17 16:19:57 -080099 // Decode remaining lines.
100 size_t i;
101 for (i = 1; i < lines.size(); i++) {
102 std::vector<string> terms;
103 base::SplitStringAlongWhitespace(lines[i], &terms);
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700104
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800105 if (terms[0] == "Range:") {
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500106 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800107 string &range = terms[1];
108 LOG(INFO) << "range attribute: " << range;
109 CHECK(StartsWithASCII(range, "bytes=", true) &&
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800110 range.find('-') != string::npos);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800111 request->start_offset = atoll(range.c_str() + strlen("bytes="));
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800112 // Decode end offset and increment it by one (so it is non-inclusive).
113 if (range.find('-') < range.length() - 1)
114 request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800115 request->return_code = kHttpResponsePartialContent;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800116 std::string tmp_str = StringPrintf("decoded range offsets: start=%jd "
117 "end=", request->start_offset);
118 if (request->end_offset > 0)
119 base::StringAppendF(&tmp_str, "%jd (non-inclusive)",
120 request->end_offset);
121 else
122 base::StringAppendF(&tmp_str, "unspecified");
123 LOG(INFO) << tmp_str;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800124 } else if (terms[0] == "Host:") {
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500125 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800126 request->host = terms[1];
127 LOG(INFO) << "host attribute: " << request->host;
128 } else {
129 LOG(WARNING) << "ignoring HTTP attribute: `" << lines[i] << "'";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000130 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800132
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700133 return true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000134}
135
136string Itoa(off_t num) {
137 char buf[100] = {0};
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700138 snprintf(buf, sizeof(buf), "%" PRIi64, num);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000139 return buf;
140}
141
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800142// Writes a string into a file. Returns total number of bytes written or -1 if a
143// write error occurred.
144ssize_t WriteString(int fd, const string& str) {
145 const size_t total_size = str.size();
146 size_t remaining_size = total_size;
147 char const *data = str.data();
Gilad Arnold48085ba2011-11-16 09:36:08 -0800148
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800149 while (remaining_size) {
150 ssize_t written = write(fd, data, remaining_size);
151 if (written < 0) {
152 perror("write");
153 LOG(INFO) << "write failed";
154 return -1;
155 }
156 data += written;
157 remaining_size -= written;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000158 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800159
160 return total_size;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000161}
162
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800163// Writes the headers of an HTTP response into a file.
164ssize_t WriteHeaders(int fd, const off_t start_offset, const off_t end_offset,
165 HttpResponseCode return_code) {
166 ssize_t written = 0, ret;
167
168 ret = WriteString(fd,
169 string("HTTP/1.1 ") + Itoa(return_code) + " " +
170 GetHttpResponseDescription(return_code) +
171 EOL
172 "Content-Type: application/octet-stream" EOL);
173 if (ret < 0)
174 return -1;
175 written += ret;
176
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800177 // Compute content legnth.
178 const off_t content_length = end_offset - start_offset;;
179
180 // A start offset that equals the end offset indicates that the response
181 // should contain the full range of bytes in the requested resource.
182 if (start_offset || start_offset == end_offset) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800183 ret = WriteString(fd,
184 string("Accept-Ranges: bytes" EOL
185 "Content-Range: bytes ") +
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800186 Itoa(start_offset == end_offset ? 0 : start_offset) +
187 "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) +
188 EOL);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800189 if (ret < 0)
190 return -1;
191 written += ret;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800192 }
193
194 ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) +
195 EOL EOL);
196 if (ret < 0)
197 return -1;
198 written += ret;
199
200 return written;
201}
202
203// Writes a predetermined payload of lines of ascending bytes to a file. The
204// first byte of output is appropriately offset with respect to the request line
205// length. Returns the number of successfully written bytes.
206size_t WritePayload(int fd, const off_t start_offset, const off_t end_offset,
207 const char first_byte, const size_t line_len) {
208 CHECK_LE(start_offset, end_offset);
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500209 CHECK_GT(line_len, static_cast<size_t>(0));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800210
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800211 LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `"
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800212 << first_byte << "', offset range " << start_offset << " -> "
213 << end_offset;
214
215 // Populate line of ascending characters.
216 string line;
217 line.reserve(line_len);
218 char byte = first_byte;
219 size_t i;
220 for (i = 0; i < line_len; i++)
221 line += byte++;
222
223 const size_t total_len = end_offset - start_offset;
224 size_t remaining_len = total_len;
225 bool success = true;
226
227 // If start offset is not aligned with line boundary, output partial line up
228 // to the first line boundary.
229 size_t start_modulo = start_offset % line_len;
230 if (start_modulo) {
231 string partial = line.substr(start_modulo, remaining_len);
232 ssize_t ret = WriteString(fd, partial);
233 if ((success = (ret >= 0 && (size_t) ret == partial.length())))
234 remaining_len -= partial.length();
235 }
236
237 // Output full lines up to the maximal line boundary below the end offset.
238 while (success && remaining_len >= line_len) {
239 ssize_t ret = WriteString(fd, line);
240 if ((success = (ret >= 0 && (size_t) ret == line_len)))
241 remaining_len -= line_len;
242 }
243
244 // Output a partial line up to the end offset.
245 if (success && remaining_len) {
246 string partial = line.substr(0, remaining_len);
247 ssize_t ret = WriteString(fd, partial);
248 if ((success = (ret >= 0 && (size_t) ret == partial.length())))
249 remaining_len -= partial.length();
250 }
251
252 return (total_len - remaining_len);
253}
254
255// Write default payload lines of the form 'abcdefghij'.
256inline size_t WritePayload(int fd, const off_t start_offset,
257 const off_t end_offset) {
258 return WritePayload(fd, start_offset, end_offset, 'a', 10);
259}
260
261// Send an empty response, then kill the server.
262void HandleQuit(int fd) {
263 WriteHeaders(fd, 0, 0, kHttpResponseOk);
Jay Srinivasan135a58b2012-07-13 12:46:49 -0700264 LOG(INFO) << "pid(" << getpid() << "): HTTP server exiting ...";
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700265 exit(RC_OK);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000266}
267
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800268
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800269// Generates an HTTP response with payload corresponding to requested offsets
270// and length. Optionally, truncate the payload at a given length and add a
271// pause midway through the transfer. Returns the total number of bytes
272// delivered or -1 for error.
273ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length,
274 const size_t truncate_length, const int sleep_every,
275 const int sleep_secs) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800276 ssize_t ret;
277 size_t written = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000278
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800279 // Obtain start offset, make sure it is within total payload length.
280 const size_t start_offset = request.start_offset;
281 if (start_offset >= total_length) {
282 LOG(WARNING) << "start offset (" << start_offset
283 << ") exceeds total length (" << total_length
284 << "), generating error response ("
285 << kHttpResponseReqRangeNotSat << ")";
286 return WriteHeaders(fd, total_length, total_length,
287 kHttpResponseReqRangeNotSat);
288 }
289
290 // Obtain end offset, adjust to fit in total payload length and ensure it does
291 // not preceded the start offset.
292 size_t end_offset = (request.end_offset > 0 ?
293 request.end_offset : total_length);
294 if (end_offset < start_offset) {
295 LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset ("
296 << start_offset << "), generating error response";
297 return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest);
298 }
299 if (end_offset > total_length) {
300 LOG(INFO) << "requested end offset (" << end_offset
301 << ") exceeds total length (" << total_length << "), adjusting";
302 end_offset = total_length;
303 }
304
305 // Generate headers
306 LOG(INFO) << "generating response header: range=" << start_offset << "-"
307 << (end_offset - 1) << "/" << (end_offset - start_offset)
308 << ", return code=" << request.return_code;
309 if ((ret = WriteHeaders(fd, start_offset, end_offset,
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800310 request.return_code)) < 0)
311 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800312 LOG(INFO) << ret << " header bytes written";
313 written += ret;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800314
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800315 // Compute payload length, truncate as necessary.
316 size_t payload_length = end_offset - start_offset;
317 if (truncate_length > 0 && truncate_length < payload_length) {
318 LOG(INFO) << "truncating request payload length (" << payload_length
319 << ") at " << truncate_length;
320 payload_length = truncate_length;
321 end_offset = start_offset + payload_length;
322 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800323
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800324 LOG(INFO) << "generating response payload: range=" << start_offset << "-"
325 << (end_offset - 1) << "/" << (end_offset - start_offset);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800326
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800327 // Decide about optional midway delay.
328 if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 &&
329 start_offset % (truncate_length * sleep_every) == 0) {
330 const off_t midway_offset = start_offset + payload_length / 2;
331
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800332 if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0)
333 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800334 LOG(INFO) << ret << " payload bytes written (first chunk)";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800335 written += ret;
336
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800337 LOG(INFO) << "sleeping for " << sleep_secs << " seconds...";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800338 sleep(sleep_secs);
339
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800340 if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0)
341 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800342 LOG(INFO) << ret << " payload bytes written (second chunk)";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800343 written += ret;
344 } else {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800345 if ((ret = WritePayload(fd, start_offset, end_offset)) < 0)
346 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800347 LOG(INFO) << ret << " payload bytes written";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800348 written += ret;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000349 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800350
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800351 LOG(INFO) << "response generation complete, " << written
352 << " total bytes written";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800353 return written;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000354}
355
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800356ssize_t HandleGet(int fd, const HttpRequest& request,
357 const size_t total_length) {
358 return HandleGet(fd, request, total_length, 0, 0, 0);
359}
360
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700361// Handles /redirect/<code>/<url> requests by returning the specified
362// redirect <code> with a location pointing to /<url>.
363void HandleRedirect(int fd, const HttpRequest& request) {
364 LOG(INFO) << "Redirecting...";
365 string url = request.url;
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500366 CHECK_EQ(static_cast<size_t>(0), url.find("/redirect/"));
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700367 url.erase(0, strlen("/redirect/"));
368 string::size_type url_start = url.find('/');
369 CHECK_NE(url_start, string::npos);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800370 HttpResponseCode code = StringToHttpResponseCode(url.c_str());
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700371 url.erase(0, url_start);
372 url = "http://" + request.host + url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800373 const char *status = GetHttpResponseDescription(code);
374 if (!status)
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700375 CHECK(false) << "Unrecognized redirection code: " << code;
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700376 LOG(INFO) << "Code: " << code << " " << status;
377 LOG(INFO) << "New URL: " << url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800378
379 ssize_t ret;
380 if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " +
381 status + EOL)) < 0)
382 return;
383 WriteString(fd, "Location: " + url + EOL);
384}
385
386// Generate a page not found error response with actual text payload. Return
387// number of bytes written or -1 for error.
388ssize_t HandleError(int fd, const HttpRequest& request) {
389 LOG(INFO) << "Generating error HTTP response";
390
391 ssize_t ret;
392 size_t written = 0;
393
394 const string data("This is an error page.");
395
396 if ((ret = WriteHeaders(fd, 0, data.size(), kHttpResponseNotFound)) < 0)
397 return -1;
398 written += ret;
399
400 if ((ret = WriteString(fd, data)) < 0)
401 return -1;
402 written += ret;
403
404 return written;
405}
406
407// Generate an error response if the requested offset is nonzero, up to a given
408// maximal number of successive failures. The error generated is an "Internal
409// Server Error" (500).
410ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request,
411 size_t end_offset, int max_fails) {
412 static int num_fails = 0;
413
414 if (request.start_offset > 0 && num_fails < max_fails) {
415 LOG(INFO) << "Generating error HTTP response";
416
417 ssize_t ret;
418 size_t written = 0;
419
420 const string data("This is an error page.");
421
422 if ((ret = WriteHeaders(fd, 0, data.size(),
423 kHttpResponseInternalServerError)) < 0)
424 return -1;
425 written += ret;
426
427 if ((ret = WriteString(fd, data)) < 0)
428 return -1;
429 written += ret;
430
431 num_fails++;
432 return written;
433 } else {
434 num_fails = 0;
435 return HandleGet(fd, request, end_offset);
436 }
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700437}
438
adlr@google.com3defe6a2009-12-04 20:57:17 +0000439void HandleDefault(int fd, const HttpRequest& request) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800440 const off_t start_offset = request.start_offset;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000441 const string data("unhandled path");
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800442 const size_t size = data.size();
443 ssize_t ret;
444
445 if ((ret = WriteHeaders(fd, start_offset, size, request.return_code)) < 0)
446 return;
447 WriteString(fd, (start_offset < static_cast<off_t>(size) ?
448 data.substr(start_offset) : ""));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000449}
450
Gilad Arnold48085ba2011-11-16 09:36:08 -0800451
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800452// Break a URL into terms delimited by slashes.
453class UrlTerms {
454 public:
455 UrlTerms(string &url, size_t num_terms) {
456 // URL must be non-empty and start with a slash.
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500457 CHECK_GT(url.size(), static_cast<size_t>(0));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800458 CHECK_EQ(url[0], '/');
459
460 // Split it into terms delimited by slashes, omitting the preceeding slash.
461 base::SplitStringDontTrim(url.substr(1), '/', &terms);
462
463 // Ensure expected length.
464 CHECK_EQ(terms.size(), num_terms);
465 }
466
467 inline string Get(const off_t index) const {
468 return terms[index];
469 }
470 inline const char *GetCStr(const off_t index) const {
471 return Get(index).c_str();
472 }
473 inline int GetInt(const off_t index) const {
474 return atoi(GetCStr(index));
475 }
476 inline long GetLong(const off_t index) const {
477 return atol(GetCStr(index));
478 }
479
480 private:
481 std::vector<string> terms;
482};
Gilad Arnold48085ba2011-11-16 09:36:08 -0800483
adlr@google.com3defe6a2009-12-04 20:57:17 +0000484void HandleConnection(int fd) {
485 HttpRequest request;
486 ParseRequest(fd, &request);
487
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800488 string &url = request.url;
Jay Srinivasan135a58b2012-07-13 12:46:49 -0700489 LOG(INFO) << "pid(" << getpid() << "): handling url " << url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800490 if (url == "/quitquitquit") {
491 HandleQuit(fd);
492 } else if (StartsWithASCII(url, "/download/", true)) {
493 const UrlTerms terms(url, 2);
494 HandleGet(fd, request, terms.GetLong(1));
495 } else if (StartsWithASCII(url, "/flaky/", true)) {
496 const UrlTerms terms(url, 5);
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800497 HandleGet(fd, request, terms.GetLong(1), terms.GetLong(2), terms.GetLong(3),
498 terms.GetLong(4));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800499 } else if (url.find("/redirect/") == 0) {
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700500 HandleRedirect(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800501 } else if (url == "/error") {
Gilad Arnold48085ba2011-11-16 09:36:08 -0800502 HandleError(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800503 } else if (StartsWithASCII(url, "/error-if-offset/", true)) {
504 const UrlTerms terms(url, 3);
505 HandleErrorIfOffset(fd, request, terms.GetLong(1), terms.GetInt(2));
506 } else {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000507 HandleDefault(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800508 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000509
510 close(fd);
511}
512
513} // namespace chromeos_update_engine
514
515using namespace chromeos_update_engine;
516
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700517void usage(const char *prog_arg) {
518 static const char usage_str[] =
519 "Usage: %s [ PORT ]\n"
520 "where PORT is an integer between %ld and %ld (default is %d).\n";
521 fprintf(stderr, usage_str, basename(prog_arg), kPortMin, kPortMax,
522 kPortDefault);
523}
524
adlr@google.com3defe6a2009-12-04 20:57:17 +0000525int main(int argc, char** argv) {
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700526 // Check invocation.
527 if (argc > 2)
528 errx(RC_BAD_ARGS, "unexpected number of arguments (use -h for usage)");
529
530 // Parse inbound port number argument (in host byte-order, as of yet).
531 in_port_t port = kPortDefault;
532 if (argc == 2) {
533 if (!strcmp(argv[1], "-h")) {
534 usage(argv[0]);
535 exit(RC_OK);
536 }
537
538 char *end_ptr;
539 long raw_port = strtol(argv[1], &end_ptr, 10);
540 if (*end_ptr || raw_port < kPortMin || raw_port > kPortMax)
541 errx(RC_BAD_ARGS, "invalid port: %s", argv[1]);
542 port = static_cast<int>(raw_port);
543 }
544
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700545 // Ignore SIGPIPE on write() to sockets.
546 signal(SIGPIPE, SIG_IGN);
Darin Petkovf67bb1f2010-11-08 16:10:26 -0800547
adlr@google.com3defe6a2009-12-04 20:57:17 +0000548 socklen_t clilen;
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700549 struct sockaddr_in server_addr = sockaddr_in();
550 struct sockaddr_in client_addr = sockaddr_in();
adlr@google.com3defe6a2009-12-04 20:57:17 +0000551
552 int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
553 if (listen_fd < 0)
554 LOG(FATAL) << "socket() failed";
555
556 server_addr.sin_family = AF_INET;
557 server_addr.sin_addr.s_addr = INADDR_ANY;
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700558 server_addr.sin_port = htons(port); // byte-order conversion is necessary!
adlr@google.com3defe6a2009-12-04 20:57:17 +0000559
560 {
561 // Get rid of "Address in use" error
562 int tr = 1;
563 if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr,
564 sizeof(int)) == -1) {
565 perror("setsockopt");
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700566 exit(RC_ERR_SETSOCKOPT);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000567 }
568 }
569
570 if (bind(listen_fd, reinterpret_cast<struct sockaddr *>(&server_addr),
571 sizeof(server_addr)) < 0) {
572 perror("bind");
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700573 exit(RC_ERR_BIND);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000574 }
575 CHECK_EQ(listen(listen_fd,5), 0);
576 while (1) {
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700577 LOG(INFO) << "pid(" << getpid()
578 << "): waiting to accept new connection on port " << port;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000579 clilen = sizeof(client_addr);
580 int client_fd = accept(listen_fd,
581 (struct sockaddr *) &client_addr,
582 &clilen);
583 LOG(INFO) << "got past accept";
584 if (client_fd < 0)
585 LOG(FATAL) << "ERROR on accept";
586 HandleConnection(client_fd);
587 }
588 return 0;
589}