blob: a94f5542eb8f92d362b659dc249545dde948878b [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>
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070015#include <fcntl.h>
Andrew de los Reyes08c4e272010-04-15 14:02:17 -070016#include <inttypes.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070017#include <netinet/in.h>
18#include <signal.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070022#include <sys/socket.h>
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070023#include <sys/stat.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070024#include <sys/types.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000025#include <unistd.h>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070026
adlr@google.com3defe6a2009-12-04 20:57:17 +000027#include <algorithm>
28#include <string>
29#include <vector>
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -070030
31#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070032#include <base/strings/string_split.h>
33#include <base/strings/string_util.h>
34#include <base/strings/stringprintf.h>
Gilad Arnold9bedeb52011-11-17 16:19:57 -080035
36#include "update_engine/http_common.h"
Gilad Arnold9bedeb52011-11-17 16:19:57 -080037
adlr@google.com3defe6a2009-12-04 20:57:17 +000038
Gilad Arnolde4ad2502011-12-29 17:08:54 -080039// HTTP end-of-line delimiter; sorry, this needs to be a macro.
40#define EOL "\r\n"
41
adlr@google.com3defe6a2009-12-04 20:57:17 +000042using std::min;
43using std::string;
44using std::vector;
45
Gilad Arnold9bedeb52011-11-17 16:19:57 -080046
adlr@google.com3defe6a2009-12-04 20:57:17 +000047namespace chromeos_update_engine {
48
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070049static const char* kListeningMsgPrefix = "listening on port ";
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070050
51enum {
52 RC_OK = 0,
53 RC_BAD_ARGS,
54 RC_ERR_READ,
55 RC_ERR_SETSOCKOPT,
56 RC_ERR_BIND,
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070057 RC_ERR_LISTEN,
58 RC_ERR_GETSOCKNAME,
59 RC_ERR_REPORT,
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070060};
61
adlr@google.com3defe6a2009-12-04 20:57:17 +000062struct HttpRequest {
Gilad Arnolde4ad2502011-12-29 17:08:54 -080063 HttpRequest()
64 : start_offset(0), end_offset(0), return_code(kHttpResponseOk) {}
Darin Petkov41c2fcf2010-08-25 13:14:48 -070065 string host;
adlr@google.com3defe6a2009-12-04 20:57:17 +000066 string url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -080067 off_t start_offset;
Gilad Arnolde4ad2502011-12-29 17:08:54 -080068 off_t end_offset; // non-inclusive, zero indicates unspecified.
Gilad Arnold9bedeb52011-11-17 16:19:57 -080069 HttpResponseCode return_code;
adlr@google.com3defe6a2009-12-04 20:57:17 +000070};
71
adlr@google.com3defe6a2009-12-04 20:57:17 +000072bool ParseRequest(int fd, HttpRequest* request) {
73 string headers;
Gilad Arnold9bedeb52011-11-17 16:19:57 -080074 do {
75 char buf[1024];
76 ssize_t r = read(fd, buf, sizeof(buf));
adlr@google.com3defe6a2009-12-04 20:57:17 +000077 if (r < 0) {
78 perror("read");
Gilad Arnold97bdb3f2013-06-30 23:05:50 -070079 exit(RC_ERR_READ);
adlr@google.com3defe6a2009-12-04 20:57:17 +000080 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -080081 headers.append(buf, r);
82 } while (!EndsWith(headers, EOL EOL, true));
adlr@google.com3defe6a2009-12-04 20:57:17 +000083
Gilad Arnold9bedeb52011-11-17 16:19:57 -080084 LOG(INFO) << "got headers:\n--8<------8<------8<------8<----\n"
85 << headers
86 << "\n--8<------8<------8<------8<----";
adlr@google.com3defe6a2009-12-04 20:57:17 +000087
Gilad Arnold9bedeb52011-11-17 16:19:57 -080088 // Break header into lines.
89 std::vector<string> lines;
90 base::SplitStringUsingSubstr(
91 headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
adlr@google.com3defe6a2009-12-04 20:57:17 +000092
Gilad Arnold9bedeb52011-11-17 16:19:57 -080093 // Decode URL line.
94 std::vector<string> terms;
95 base::SplitStringAlongWhitespace(lines[0], &terms);
Mike Frysinger0f9547d2012-02-16 12:11:37 -050096 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(3));
Gilad Arnold9bedeb52011-11-17 16:19:57 -080097 CHECK_EQ(terms[0], "GET");
98 request->url = terms[1];
99 LOG(INFO) << "URL: " << request->url;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000100
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800101 // Decode remaining lines.
102 size_t i;
103 for (i = 1; i < lines.size(); i++) {
104 std::vector<string> terms;
105 base::SplitStringAlongWhitespace(lines[i], &terms);
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700106
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800107 if (terms[0] == "Range:") {
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500108 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800109 string &range = terms[1];
110 LOG(INFO) << "range attribute: " << range;
111 CHECK(StartsWithASCII(range, "bytes=", true) &&
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800112 range.find('-') != string::npos);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800113 request->start_offset = atoll(range.c_str() + strlen("bytes="));
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800114 // Decode end offset and increment it by one (so it is non-inclusive).
115 if (range.find('-') < range.length() - 1)
116 request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800117 request->return_code = kHttpResponsePartialContent;
Alex Vakulenko75039d72014-03-25 12:36:28 -0700118 std::string tmp_str = base::StringPrintf("decoded range offsets: "
119 "start=%jd end=",
120 (intmax_t)request->start_offset);
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800121 if (request->end_offset > 0)
122 base::StringAppendF(&tmp_str, "%jd (non-inclusive)",
Alex Vakulenko75039d72014-03-25 12:36:28 -0700123 (intmax_t)request->end_offset);
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800124 else
125 base::StringAppendF(&tmp_str, "unspecified");
126 LOG(INFO) << tmp_str;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800127 } else if (terms[0] == "Host:") {
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500128 CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800129 request->host = terms[1];
130 LOG(INFO) << "host attribute: " << request->host;
131 } else {
132 LOG(WARNING) << "ignoring HTTP attribute: `" << lines[i] << "'";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000133 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000134 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800135
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700136 return true;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000137}
138
139string Itoa(off_t num) {
140 char buf[100] = {0};
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700141 snprintf(buf, sizeof(buf), "%" PRIi64, num);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000142 return buf;
143}
144
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800145// Writes a string into a file. Returns total number of bytes written or -1 if a
146// write error occurred.
147ssize_t WriteString(int fd, const string& str) {
148 const size_t total_size = str.size();
149 size_t remaining_size = total_size;
150 char const *data = str.data();
Gilad Arnold48085ba2011-11-16 09:36:08 -0800151
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800152 while (remaining_size) {
153 ssize_t written = write(fd, data, remaining_size);
154 if (written < 0) {
155 perror("write");
156 LOG(INFO) << "write failed";
157 return -1;
158 }
159 data += written;
160 remaining_size -= written;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000161 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800162
163 return total_size;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000164}
165
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800166// Writes the headers of an HTTP response into a file.
167ssize_t WriteHeaders(int fd, const off_t start_offset, const off_t end_offset,
168 HttpResponseCode return_code) {
169 ssize_t written = 0, ret;
170
171 ret = WriteString(fd,
172 string("HTTP/1.1 ") + Itoa(return_code) + " " +
173 GetHttpResponseDescription(return_code) +
174 EOL
175 "Content-Type: application/octet-stream" EOL);
176 if (ret < 0)
177 return -1;
178 written += ret;
179
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800180 // Compute content legnth.
181 const off_t content_length = end_offset - start_offset;;
182
183 // A start offset that equals the end offset indicates that the response
184 // should contain the full range of bytes in the requested resource.
185 if (start_offset || start_offset == end_offset) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800186 ret = WriteString(fd,
187 string("Accept-Ranges: bytes" EOL
188 "Content-Range: bytes ") +
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800189 Itoa(start_offset == end_offset ? 0 : start_offset) +
190 "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) +
191 EOL);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800192 if (ret < 0)
193 return -1;
194 written += ret;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800195 }
196
197 ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) +
198 EOL EOL);
199 if (ret < 0)
200 return -1;
201 written += ret;
202
203 return written;
204}
205
206// Writes a predetermined payload of lines of ascending bytes to a file. The
207// first byte of output is appropriately offset with respect to the request line
208// length. Returns the number of successfully written bytes.
209size_t WritePayload(int fd, const off_t start_offset, const off_t end_offset,
210 const char first_byte, const size_t line_len) {
211 CHECK_LE(start_offset, end_offset);
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500212 CHECK_GT(line_len, static_cast<size_t>(0));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800213
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800214 LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `"
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800215 << first_byte << "', offset range " << start_offset << " -> "
216 << end_offset;
217
218 // Populate line of ascending characters.
219 string line;
220 line.reserve(line_len);
221 char byte = first_byte;
222 size_t i;
223 for (i = 0; i < line_len; i++)
224 line += byte++;
225
226 const size_t total_len = end_offset - start_offset;
227 size_t remaining_len = total_len;
228 bool success = true;
229
230 // If start offset is not aligned with line boundary, output partial line up
231 // to the first line boundary.
232 size_t start_modulo = start_offset % line_len;
233 if (start_modulo) {
234 string partial = line.substr(start_modulo, remaining_len);
235 ssize_t ret = WriteString(fd, partial);
236 if ((success = (ret >= 0 && (size_t) ret == partial.length())))
237 remaining_len -= partial.length();
238 }
239
240 // Output full lines up to the maximal line boundary below the end offset.
241 while (success && remaining_len >= line_len) {
242 ssize_t ret = WriteString(fd, line);
243 if ((success = (ret >= 0 && (size_t) ret == line_len)))
244 remaining_len -= line_len;
245 }
246
247 // Output a partial line up to the end offset.
248 if (success && remaining_len) {
249 string partial = line.substr(0, remaining_len);
250 ssize_t ret = WriteString(fd, partial);
251 if ((success = (ret >= 0 && (size_t) ret == partial.length())))
252 remaining_len -= partial.length();
253 }
254
255 return (total_len - remaining_len);
256}
257
258// Write default payload lines of the form 'abcdefghij'.
259inline size_t WritePayload(int fd, const off_t start_offset,
260 const off_t end_offset) {
261 return WritePayload(fd, start_offset, end_offset, 'a', 10);
262}
263
264// Send an empty response, then kill the server.
265void HandleQuit(int fd) {
266 WriteHeaders(fd, 0, 0, kHttpResponseOk);
Jay Srinivasan135a58b2012-07-13 12:46:49 -0700267 LOG(INFO) << "pid(" << getpid() << "): HTTP server exiting ...";
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700268 exit(RC_OK);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000269}
270
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800271
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800272// Generates an HTTP response with payload corresponding to requested offsets
273// and length. Optionally, truncate the payload at a given length and add a
274// pause midway through the transfer. Returns the total number of bytes
275// delivered or -1 for error.
276ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length,
277 const size_t truncate_length, const int sleep_every,
278 const int sleep_secs) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800279 ssize_t ret;
280 size_t written = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000281
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800282 // Obtain start offset, make sure it is within total payload length.
283 const size_t start_offset = request.start_offset;
284 if (start_offset >= total_length) {
285 LOG(WARNING) << "start offset (" << start_offset
286 << ") exceeds total length (" << total_length
287 << "), generating error response ("
288 << kHttpResponseReqRangeNotSat << ")";
289 return WriteHeaders(fd, total_length, total_length,
290 kHttpResponseReqRangeNotSat);
291 }
292
293 // Obtain end offset, adjust to fit in total payload length and ensure it does
294 // not preceded the start offset.
295 size_t end_offset = (request.end_offset > 0 ?
296 request.end_offset : total_length);
297 if (end_offset < start_offset) {
298 LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset ("
299 << start_offset << "), generating error response";
300 return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest);
301 }
302 if (end_offset > total_length) {
303 LOG(INFO) << "requested end offset (" << end_offset
304 << ") exceeds total length (" << total_length << "), adjusting";
305 end_offset = total_length;
306 }
307
308 // Generate headers
309 LOG(INFO) << "generating response header: range=" << start_offset << "-"
310 << (end_offset - 1) << "/" << (end_offset - start_offset)
311 << ", return code=" << request.return_code;
312 if ((ret = WriteHeaders(fd, start_offset, end_offset,
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800313 request.return_code)) < 0)
314 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800315 LOG(INFO) << ret << " header bytes written";
316 written += ret;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800317
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800318 // Compute payload length, truncate as necessary.
319 size_t payload_length = end_offset - start_offset;
320 if (truncate_length > 0 && truncate_length < payload_length) {
321 LOG(INFO) << "truncating request payload length (" << payload_length
322 << ") at " << truncate_length;
323 payload_length = truncate_length;
324 end_offset = start_offset + payload_length;
325 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800326
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800327 LOG(INFO) << "generating response payload: range=" << start_offset << "-"
328 << (end_offset - 1) << "/" << (end_offset - start_offset);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800329
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800330 // Decide about optional midway delay.
331 if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 &&
332 start_offset % (truncate_length * sleep_every) == 0) {
333 const off_t midway_offset = start_offset + payload_length / 2;
334
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800335 if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0)
336 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800337 LOG(INFO) << ret << " payload bytes written (first chunk)";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800338 written += ret;
339
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800340 LOG(INFO) << "sleeping for " << sleep_secs << " seconds...";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800341 sleep(sleep_secs);
342
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800343 if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0)
344 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800345 LOG(INFO) << ret << " payload bytes written (second chunk)";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800346 written += ret;
347 } else {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800348 if ((ret = WritePayload(fd, start_offset, end_offset)) < 0)
349 return -1;
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800350 LOG(INFO) << ret << " payload bytes written";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800351 written += ret;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000352 }
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800353
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800354 LOG(INFO) << "response generation complete, " << written
355 << " total bytes written";
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800356 return written;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000357}
358
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800359ssize_t HandleGet(int fd, const HttpRequest& request,
360 const size_t total_length) {
361 return HandleGet(fd, request, total_length, 0, 0, 0);
362}
363
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700364// Handles /redirect/<code>/<url> requests by returning the specified
365// redirect <code> with a location pointing to /<url>.
366void HandleRedirect(int fd, const HttpRequest& request) {
367 LOG(INFO) << "Redirecting...";
368 string url = request.url;
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500369 CHECK_EQ(static_cast<size_t>(0), url.find("/redirect/"));
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700370 url.erase(0, strlen("/redirect/"));
371 string::size_type url_start = url.find('/');
372 CHECK_NE(url_start, string::npos);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800373 HttpResponseCode code = StringToHttpResponseCode(url.c_str());
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700374 url.erase(0, url_start);
375 url = "http://" + request.host + url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800376 const char *status = GetHttpResponseDescription(code);
377 if (!status)
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700378 CHECK(false) << "Unrecognized redirection code: " << code;
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700379 LOG(INFO) << "Code: " << code << " " << status;
380 LOG(INFO) << "New URL: " << url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800381
382 ssize_t ret;
383 if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " +
384 status + EOL)) < 0)
385 return;
386 WriteString(fd, "Location: " + url + EOL);
387}
388
389// Generate a page not found error response with actual text payload. Return
390// number of bytes written or -1 for error.
391ssize_t HandleError(int fd, const HttpRequest& request) {
392 LOG(INFO) << "Generating error HTTP response";
393
394 ssize_t ret;
395 size_t written = 0;
396
397 const string data("This is an error page.");
398
399 if ((ret = WriteHeaders(fd, 0, data.size(), kHttpResponseNotFound)) < 0)
400 return -1;
401 written += ret;
402
403 if ((ret = WriteString(fd, data)) < 0)
404 return -1;
405 written += ret;
406
407 return written;
408}
409
410// Generate an error response if the requested offset is nonzero, up to a given
411// maximal number of successive failures. The error generated is an "Internal
412// Server Error" (500).
413ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request,
414 size_t end_offset, int max_fails) {
415 static int num_fails = 0;
416
417 if (request.start_offset > 0 && num_fails < max_fails) {
418 LOG(INFO) << "Generating error HTTP response";
419
420 ssize_t ret;
421 size_t written = 0;
422
423 const string data("This is an error page.");
424
425 if ((ret = WriteHeaders(fd, 0, data.size(),
426 kHttpResponseInternalServerError)) < 0)
427 return -1;
428 written += ret;
429
430 if ((ret = WriteString(fd, data)) < 0)
431 return -1;
432 written += ret;
433
434 num_fails++;
435 return written;
436 } else {
437 num_fails = 0;
438 return HandleGet(fd, request, end_offset);
439 }
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700440}
441
adlr@google.com3defe6a2009-12-04 20:57:17 +0000442void HandleDefault(int fd, const HttpRequest& request) {
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800443 const off_t start_offset = request.start_offset;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000444 const string data("unhandled path");
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800445 const size_t size = data.size();
446 ssize_t ret;
447
448 if ((ret = WriteHeaders(fd, start_offset, size, request.return_code)) < 0)
449 return;
450 WriteString(fd, (start_offset < static_cast<off_t>(size) ?
451 data.substr(start_offset) : ""));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000452}
453
Gilad Arnold48085ba2011-11-16 09:36:08 -0800454
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800455// Break a URL into terms delimited by slashes.
456class UrlTerms {
457 public:
458 UrlTerms(string &url, size_t num_terms) {
459 // URL must be non-empty and start with a slash.
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500460 CHECK_GT(url.size(), static_cast<size_t>(0));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800461 CHECK_EQ(url[0], '/');
462
463 // Split it into terms delimited by slashes, omitting the preceeding slash.
464 base::SplitStringDontTrim(url.substr(1), '/', &terms);
465
466 // Ensure expected length.
467 CHECK_EQ(terms.size(), num_terms);
468 }
469
470 inline string Get(const off_t index) const {
471 return terms[index];
472 }
473 inline const char *GetCStr(const off_t index) const {
474 return Get(index).c_str();
475 }
476 inline int GetInt(const off_t index) const {
477 return atoi(GetCStr(index));
478 }
479 inline long GetLong(const off_t index) const {
480 return atol(GetCStr(index));
481 }
482
483 private:
484 std::vector<string> terms;
485};
Gilad Arnold48085ba2011-11-16 09:36:08 -0800486
adlr@google.com3defe6a2009-12-04 20:57:17 +0000487void HandleConnection(int fd) {
488 HttpRequest request;
489 ParseRequest(fd, &request);
490
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800491 string &url = request.url;
Jay Srinivasan135a58b2012-07-13 12:46:49 -0700492 LOG(INFO) << "pid(" << getpid() << "): handling url " << url;
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800493 if (url == "/quitquitquit") {
494 HandleQuit(fd);
495 } else if (StartsWithASCII(url, "/download/", true)) {
496 const UrlTerms terms(url, 2);
497 HandleGet(fd, request, terms.GetLong(1));
498 } else if (StartsWithASCII(url, "/flaky/", true)) {
499 const UrlTerms terms(url, 5);
Gilad Arnolde4ad2502011-12-29 17:08:54 -0800500 HandleGet(fd, request, terms.GetLong(1), terms.GetLong(2), terms.GetLong(3),
501 terms.GetLong(4));
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800502 } else if (url.find("/redirect/") == 0) {
Darin Petkov41c2fcf2010-08-25 13:14:48 -0700503 HandleRedirect(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800504 } else if (url == "/error") {
Gilad Arnold48085ba2011-11-16 09:36:08 -0800505 HandleError(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800506 } else if (StartsWithASCII(url, "/error-if-offset/", true)) {
507 const UrlTerms terms(url, 3);
508 HandleErrorIfOffset(fd, request, terms.GetLong(1), terms.GetInt(2));
509 } else {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000510 HandleDefault(fd, request);
Gilad Arnold9bedeb52011-11-17 16:19:57 -0800511 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000512
513 close(fd);
514}
515
516} // namespace chromeos_update_engine
517
518using namespace chromeos_update_engine;
519
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700520
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700521void usage(const char *prog_arg) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700522 fprintf(
523 stderr,
524 "Usage: %s [ FILE ]\n"
525 "Once accepting connections, the following is written to FILE (or "
526 "stdout):\n"
527 "\"%sN\" (where N is an integer port number)\n",
528 basename(prog_arg), kListeningMsgPrefix);
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700529}
530
adlr@google.com3defe6a2009-12-04 20:57:17 +0000531int main(int argc, char** argv) {
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700532 // Check invocation.
533 if (argc > 2)
534 errx(RC_BAD_ARGS, "unexpected number of arguments (use -h for usage)");
535
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700536 // Parse (optional) argument.
537 int report_fd = STDOUT_FILENO;
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700538 if (argc == 2) {
539 if (!strcmp(argv[1], "-h")) {
540 usage(argv[0]);
541 exit(RC_OK);
542 }
543
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700544 report_fd = open(argv[1], O_WRONLY | O_CREAT, 00644);
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700545 }
546
Andrew de los Reyes3fd5d302010-10-07 20:07:18 -0700547 // Ignore SIGPIPE on write() to sockets.
548 signal(SIGPIPE, SIG_IGN);
Darin Petkovf67bb1f2010-11-08 16:10:26 -0800549
adlr@google.com3defe6a2009-12-04 20:57:17 +0000550 int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
551 if (listen_fd < 0)
552 LOG(FATAL) << "socket() failed";
553
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700554 struct sockaddr_in server_addr = sockaddr_in();
adlr@google.com3defe6a2009-12-04 20:57:17 +0000555 server_addr.sin_family = AF_INET;
556 server_addr.sin_addr.s_addr = INADDR_ANY;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700557 server_addr.sin_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000558
559 {
560 // Get rid of "Address in use" error
561 int tr = 1;
562 if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr,
563 sizeof(int)) == -1) {
564 perror("setsockopt");
Gilad Arnold97bdb3f2013-06-30 23:05:50 -0700565 exit(RC_ERR_SETSOCKOPT);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000566 }
567 }
568
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700569 // Bind the socket and set for listening.
adlr@google.com3defe6a2009-12-04 20:57:17 +0000570 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 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700575 if (listen(listen_fd, 5) < 0) {
576 perror("listen");
577 exit(RC_ERR_LISTEN);
578 }
579
580 // Check the actual port.
581 struct sockaddr_in bound_addr = sockaddr_in();
582 socklen_t bound_addr_len = sizeof(bound_addr);
583 if (getsockname(listen_fd, reinterpret_cast<struct sockaddr*>(&bound_addr),
584 &bound_addr_len) < 0) {
585 perror("getsockname");
586 exit(RC_ERR_GETSOCKNAME);
587 }
588 in_port_t port = ntohs(bound_addr.sin_port);
589
590 // Output the listening port, indicating that the server is processing
591 // requests. IMPORTANT! (a) the format of this message is as expected by some
592 // unit tests, avoid unilateral changes; (b) it is necessary to flush/sync the
593 // file to prevent the spawning process from waiting indefinitely for this
594 // message.
Alex Vakulenko75039d72014-03-25 12:36:28 -0700595 string listening_msg = base::StringPrintf("%s%hu", kListeningMsgPrefix, port);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700596 LOG(INFO) << listening_msg;
597 CHECK_EQ(write(report_fd, listening_msg.c_str(), listening_msg.length()),
598 static_cast<int>(listening_msg.length()));
599 CHECK_EQ(write(report_fd, "\n", 1), 1);
600 if (report_fd == STDOUT_FILENO)
601 fsync(report_fd);
602 else
603 close(report_fd);
604
adlr@google.com3defe6a2009-12-04 20:57:17 +0000605 while (1) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700606 LOG(INFO) << "pid(" << getpid() << "): waiting to accept new connection";
607 int client_fd = accept(listen_fd, NULL, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000608 LOG(INFO) << "got past accept";
609 if (client_fd < 0)
610 LOG(FATAL) << "ERROR on accept";
611 HandleConnection(client_fd);
612 }
613 return 0;
614}