| Alex Deymo | aea4c1c | 2015-08-19 20:24:43 -0700 | [diff] [blame] | 1 | // | 
|  | 2 | // Copyright (C) 2012 The Android Open Source Project | 
|  | 3 | // | 
|  | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | // you may not use this file except in compliance with the License. | 
|  | 6 | // You may obtain a copy of the License at | 
|  | 7 | // | 
|  | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | // | 
|  | 10 | // Unless required by applicable law or agreed to in writing, software | 
|  | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | // See the License for the specific language governing permissions and | 
|  | 14 | // limitations under the License. | 
|  | 15 | // | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 16 |  | 
|  | 17 | // This file implements a simple HTTP server. It can exhibit odd behavior | 
|  | 18 | // that's useful for testing. For example, it's useful to test that | 
|  | 19 | // the updater can continue a connection if it's dropped, or that it | 
|  | 20 | // handles very slow data transfers. | 
|  | 21 |  | 
|  | 22 | // To use this, simply make an HTTP connection to localhost:port and | 
|  | 23 | // GET a url. | 
|  | 24 |  | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 25 | #include <err.h> | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 26 | #include <errno.h> | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 27 | #include <fcntl.h> | 
| Andrew de los Reyes | 08c4e27 | 2010-04-15 14:02:17 -0700 | [diff] [blame] | 28 | #include <inttypes.h> | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 29 | #include <netinet/in.h> | 
|  | 30 | #include <signal.h> | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 31 | #include <stdio.h> | 
|  | 32 | #include <stdlib.h> | 
|  | 33 | #include <string.h> | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 34 | #include <sys/socket.h> | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 35 | #include <sys/stat.h> | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 36 | #include <sys/types.h> | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 37 | #include <unistd.h> | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 38 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 39 | #include <algorithm> | 
|  | 40 | #include <string> | 
|  | 41 | #include <vector> | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 42 |  | 
|  | 43 | #include <base/logging.h> | 
| Alex Deymo | f123ae2 | 2015-09-24 14:59:43 -0700 | [diff] [blame] | 44 | #include <base/posix/eintr_wrapper.h> | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 45 | #include <base/strings/string_split.h> | 
|  | 46 | #include <base/strings/string_util.h> | 
|  | 47 | #include <base/strings/stringprintf.h> | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 48 |  | 
| Alex Deymo | 39910dc | 2015-11-09 17:04:30 -0800 | [diff] [blame] | 49 | #include "update_engine/common/http_common.h" | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 50 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 51 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 52 | // HTTP end-of-line delimiter; sorry, this needs to be a macro. | 
|  | 53 | #define EOL "\r\n" | 
|  | 54 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 55 | using std::string; | 
|  | 56 | using std::vector; | 
|  | 57 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 58 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 59 | namespace chromeos_update_engine { | 
|  | 60 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 61 | static const char* kListeningMsgPrefix = "listening on port "; | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 62 |  | 
|  | 63 | enum { | 
|  | 64 | RC_OK = 0, | 
|  | 65 | RC_BAD_ARGS, | 
|  | 66 | RC_ERR_READ, | 
|  | 67 | RC_ERR_SETSOCKOPT, | 
|  | 68 | RC_ERR_BIND, | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 69 | RC_ERR_LISTEN, | 
|  | 70 | RC_ERR_GETSOCKNAME, | 
|  | 71 | RC_ERR_REPORT, | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 72 | }; | 
|  | 73 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 74 | struct HttpRequest { | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 75 | string raw_headers; | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 76 | string host; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 77 | string url; | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 78 | off_t start_offset{0}; | 
|  | 79 | off_t end_offset{0};  // non-inclusive, zero indicates unspecified. | 
|  | 80 | HttpResponseCode return_code{kHttpResponseOk}; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 81 | }; | 
|  | 82 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 83 | bool ParseRequest(int fd, HttpRequest* request) { | 
|  | 84 | string headers; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 85 | do { | 
|  | 86 | char buf[1024]; | 
|  | 87 | ssize_t r = read(fd, buf, sizeof(buf)); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 88 | if (r < 0) { | 
|  | 89 | perror("read"); | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 90 | exit(RC_ERR_READ); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 91 | } | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 92 | headers.append(buf, r); | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 93 | } while (!base::EndsWith(headers, EOL EOL, base::CompareCase::SENSITIVE)); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 94 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 95 | LOG(INFO) << "got headers:\n--8<------8<------8<------8<----\n" | 
|  | 96 | << headers | 
|  | 97 | << "\n--8<------8<------8<------8<----"; | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 98 | request->raw_headers = headers; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 99 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 100 | // Break header into lines. | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 101 | vector<string> lines; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 102 | base::SplitStringUsingSubstr( | 
|  | 103 | headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 104 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 105 | // Decode URL line. | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 106 | vector<string> terms = base::SplitString(lines[0], base::kWhitespaceASCII, | 
|  | 107 | base::KEEP_WHITESPACE, | 
|  | 108 | base::SPLIT_WANT_NONEMPTY); | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 109 | CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(3)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 110 | CHECK_EQ(terms[0], "GET"); | 
|  | 111 | request->url = terms[1]; | 
|  | 112 | LOG(INFO) << "URL: " << request->url; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 113 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 114 | // Decode remaining lines. | 
|  | 115 | size_t i; | 
|  | 116 | for (i = 1; i < lines.size(); i++) { | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 117 | terms = base::SplitString(lines[i], base::kWhitespaceASCII, | 
|  | 118 | base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 119 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 120 | if (terms[0] == "Range:") { | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 121 | CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 122 | string &range = terms[1]; | 
|  | 123 | LOG(INFO) << "range attribute: " << range; | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 124 | CHECK(base::StartsWith(range, "bytes=", base::CompareCase::SENSITIVE) && | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 125 | range.find('-') != string::npos); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 126 | request->start_offset = atoll(range.c_str() + strlen("bytes=")); | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 127 | // Decode end offset and increment it by one (so it is non-inclusive). | 
|  | 128 | if (range.find('-') < range.length() - 1) | 
|  | 129 | request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 130 | request->return_code = kHttpResponsePartialContent; | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 131 | string tmp_str = base::StringPrintf("decoded range offsets: " | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 132 | "start=%jd end=", | 
|  | 133 | (intmax_t)request->start_offset); | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 134 | if (request->end_offset > 0) | 
|  | 135 | base::StringAppendF(&tmp_str, "%jd (non-inclusive)", | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 136 | (intmax_t)request->end_offset); | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 137 | else | 
|  | 138 | base::StringAppendF(&tmp_str, "unspecified"); | 
|  | 139 | LOG(INFO) << tmp_str; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 140 | } else if (terms[0] == "Host:") { | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 141 | CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 142 | request->host = terms[1]; | 
|  | 143 | LOG(INFO) << "host attribute: " << request->host; | 
|  | 144 | } else { | 
|  | 145 | LOG(WARNING) << "ignoring HTTP attribute: `" << lines[i] << "'"; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 146 | } | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 147 | } | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 148 |  | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 149 | return true; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 150 | } | 
|  | 151 |  | 
|  | 152 | string Itoa(off_t num) { | 
|  | 153 | char buf[100] = {0}; | 
| Andrew de los Reyes | 08c4e27 | 2010-04-15 14:02:17 -0700 | [diff] [blame] | 154 | snprintf(buf, sizeof(buf), "%" PRIi64, num); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 155 | return buf; | 
|  | 156 | } | 
|  | 157 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 158 | // Writes a string into a file. Returns total number of bytes written or -1 if a | 
|  | 159 | // write error occurred. | 
|  | 160 | ssize_t WriteString(int fd, const string& str) { | 
|  | 161 | const size_t total_size = str.size(); | 
|  | 162 | size_t remaining_size = total_size; | 
|  | 163 | char const *data = str.data(); | 
| Gilad Arnold | 48085ba | 2011-11-16 09:36:08 -0800 | [diff] [blame] | 164 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 165 | while (remaining_size) { | 
|  | 166 | ssize_t written = write(fd, data, remaining_size); | 
|  | 167 | if (written < 0) { | 
|  | 168 | perror("write"); | 
|  | 169 | LOG(INFO) << "write failed"; | 
|  | 170 | return -1; | 
|  | 171 | } | 
|  | 172 | data += written; | 
|  | 173 | remaining_size -= written; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 174 | } | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 175 |  | 
|  | 176 | return total_size; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 177 | } | 
|  | 178 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 179 | // Writes the headers of an HTTP response into a file. | 
|  | 180 | ssize_t WriteHeaders(int fd, const off_t start_offset, const off_t end_offset, | 
|  | 181 | HttpResponseCode return_code) { | 
|  | 182 | ssize_t written = 0, ret; | 
|  | 183 |  | 
|  | 184 | ret = WriteString(fd, | 
|  | 185 | string("HTTP/1.1 ") + Itoa(return_code) + " " + | 
|  | 186 | GetHttpResponseDescription(return_code) + | 
|  | 187 | EOL | 
|  | 188 | "Content-Type: application/octet-stream" EOL); | 
|  | 189 | if (ret < 0) | 
|  | 190 | return -1; | 
|  | 191 | written += ret; | 
|  | 192 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 193 | // Compute content legnth. | 
|  | 194 | const off_t content_length = end_offset - start_offset;; | 
|  | 195 |  | 
|  | 196 | // A start offset that equals the end offset indicates that the response | 
|  | 197 | // should contain the full range of bytes in the requested resource. | 
|  | 198 | if (start_offset || start_offset == end_offset) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 199 | ret = WriteString(fd, | 
|  | 200 | string("Accept-Ranges: bytes" EOL | 
|  | 201 | "Content-Range: bytes ") + | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 202 | Itoa(start_offset == end_offset ? 0 : start_offset) + | 
|  | 203 | "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) + | 
|  | 204 | EOL); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 205 | if (ret < 0) | 
|  | 206 | return -1; | 
|  | 207 | written += ret; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 208 | } | 
|  | 209 |  | 
|  | 210 | ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) + | 
|  | 211 | EOL EOL); | 
|  | 212 | if (ret < 0) | 
|  | 213 | return -1; | 
|  | 214 | written += ret; | 
|  | 215 |  | 
|  | 216 | return written; | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | // Writes a predetermined payload of lines of ascending bytes to a file. The | 
|  | 220 | // first byte of output is appropriately offset with respect to the request line | 
|  | 221 | // length.  Returns the number of successfully written bytes. | 
|  | 222 | size_t WritePayload(int fd, const off_t start_offset, const off_t end_offset, | 
|  | 223 | const char first_byte, const size_t line_len) { | 
|  | 224 | CHECK_LE(start_offset, end_offset); | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 225 | CHECK_GT(line_len, static_cast<size_t>(0)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 226 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 227 | LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `" | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 228 | << first_byte << "', offset range " << start_offset << " -> " | 
|  | 229 | << end_offset; | 
|  | 230 |  | 
|  | 231 | // Populate line of ascending characters. | 
|  | 232 | string line; | 
|  | 233 | line.reserve(line_len); | 
|  | 234 | char byte = first_byte; | 
|  | 235 | size_t i; | 
|  | 236 | for (i = 0; i < line_len; i++) | 
|  | 237 | line += byte++; | 
|  | 238 |  | 
|  | 239 | const size_t total_len = end_offset - start_offset; | 
|  | 240 | size_t remaining_len = total_len; | 
|  | 241 | bool success = true; | 
|  | 242 |  | 
|  | 243 | // If start offset is not aligned with line boundary, output partial line up | 
|  | 244 | // to the first line boundary. | 
|  | 245 | size_t start_modulo = start_offset % line_len; | 
|  | 246 | if (start_modulo) { | 
|  | 247 | string partial = line.substr(start_modulo, remaining_len); | 
|  | 248 | ssize_t ret = WriteString(fd, partial); | 
|  | 249 | if ((success = (ret >= 0 && (size_t) ret == partial.length()))) | 
|  | 250 | remaining_len -= partial.length(); | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | // Output full lines up to the maximal line boundary below the end offset. | 
|  | 254 | while (success && remaining_len >= line_len) { | 
|  | 255 | ssize_t ret = WriteString(fd, line); | 
|  | 256 | if ((success = (ret >= 0 && (size_t) ret == line_len))) | 
|  | 257 | remaining_len -= line_len; | 
|  | 258 | } | 
|  | 259 |  | 
|  | 260 | // Output a partial line up to the end offset. | 
|  | 261 | if (success && remaining_len) { | 
|  | 262 | string partial = line.substr(0, remaining_len); | 
|  | 263 | ssize_t ret = WriteString(fd, partial); | 
|  | 264 | if ((success = (ret >= 0 && (size_t) ret == partial.length()))) | 
|  | 265 | remaining_len -= partial.length(); | 
|  | 266 | } | 
|  | 267 |  | 
|  | 268 | return (total_len - remaining_len); | 
|  | 269 | } | 
|  | 270 |  | 
|  | 271 | // Write default payload lines of the form 'abcdefghij'. | 
|  | 272 | inline size_t WritePayload(int fd, const off_t start_offset, | 
|  | 273 | const off_t end_offset) { | 
|  | 274 | return WritePayload(fd, start_offset, end_offset, 'a', 10); | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | // Send an empty response, then kill the server. | 
|  | 278 | void HandleQuit(int fd) { | 
|  | 279 | WriteHeaders(fd, 0, 0, kHttpResponseOk); | 
| Jay Srinivasan | 135a58b | 2012-07-13 12:46:49 -0700 | [diff] [blame] | 280 | LOG(INFO) << "pid(" << getpid() <<  "): HTTP server exiting ..."; | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 281 | exit(RC_OK); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 282 | } | 
|  | 283 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 284 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 285 | // Generates an HTTP response with payload corresponding to requested offsets | 
|  | 286 | // and length.  Optionally, truncate the payload at a given length and add a | 
|  | 287 | // pause midway through the transfer.  Returns the total number of bytes | 
|  | 288 | // delivered or -1 for error. | 
|  | 289 | ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length, | 
|  | 290 | const size_t truncate_length, const int sleep_every, | 
|  | 291 | const int sleep_secs) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 292 | ssize_t ret; | 
|  | 293 | size_t written = 0; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 294 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 295 | // Obtain start offset, make sure it is within total payload length. | 
|  | 296 | const size_t start_offset = request.start_offset; | 
|  | 297 | if (start_offset >= total_length) { | 
|  | 298 | LOG(WARNING) << "start offset (" << start_offset | 
|  | 299 | << ") exceeds total length (" << total_length | 
|  | 300 | << "), generating error response (" | 
|  | 301 | << kHttpResponseReqRangeNotSat << ")"; | 
|  | 302 | return WriteHeaders(fd, total_length, total_length, | 
|  | 303 | kHttpResponseReqRangeNotSat); | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | // Obtain end offset, adjust to fit in total payload length and ensure it does | 
|  | 307 | // not preceded the start offset. | 
|  | 308 | size_t end_offset = (request.end_offset > 0 ? | 
|  | 309 | request.end_offset : total_length); | 
|  | 310 | if (end_offset < start_offset) { | 
|  | 311 | LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset (" | 
|  | 312 | << start_offset << "), generating error response"; | 
|  | 313 | return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest); | 
|  | 314 | } | 
|  | 315 | if (end_offset > total_length) { | 
|  | 316 | LOG(INFO) << "requested end offset (" << end_offset | 
|  | 317 | << ") exceeds total length (" << total_length << "), adjusting"; | 
|  | 318 | end_offset = total_length; | 
|  | 319 | } | 
|  | 320 |  | 
|  | 321 | // Generate headers | 
|  | 322 | LOG(INFO) << "generating response header: range=" << start_offset << "-" | 
|  | 323 | << (end_offset - 1) << "/" << (end_offset - start_offset) | 
|  | 324 | << ", return code=" << request.return_code; | 
|  | 325 | if ((ret = WriteHeaders(fd, start_offset, end_offset, | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 326 | request.return_code)) < 0) | 
|  | 327 | return -1; | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 328 | LOG(INFO) << ret << " header bytes written"; | 
|  | 329 | written += ret; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 330 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 331 | // Compute payload length, truncate as necessary. | 
|  | 332 | size_t payload_length = end_offset - start_offset; | 
|  | 333 | if (truncate_length > 0 && truncate_length < payload_length) { | 
|  | 334 | LOG(INFO) << "truncating request payload length (" << payload_length | 
|  | 335 | << ") at " << truncate_length; | 
|  | 336 | payload_length = truncate_length; | 
|  | 337 | end_offset = start_offset + payload_length; | 
|  | 338 | } | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 339 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 340 | LOG(INFO) << "generating response payload: range=" << start_offset << "-" | 
|  | 341 | << (end_offset - 1) << "/" << (end_offset - start_offset); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 342 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 343 | // Decide about optional midway delay. | 
|  | 344 | if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 && | 
|  | 345 | start_offset % (truncate_length * sleep_every) == 0) { | 
|  | 346 | const off_t midway_offset = start_offset + payload_length / 2; | 
|  | 347 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 348 | if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0) | 
|  | 349 | return -1; | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 350 | LOG(INFO) << ret << " payload bytes written (first chunk)"; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 351 | written += ret; | 
|  | 352 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 353 | LOG(INFO) << "sleeping for " << sleep_secs << " seconds..."; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 354 | sleep(sleep_secs); | 
|  | 355 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 356 | if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0) | 
|  | 357 | return -1; | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 358 | LOG(INFO) << ret << " payload bytes written (second chunk)"; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 359 | written += ret; | 
|  | 360 | } else { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 361 | if ((ret = WritePayload(fd, start_offset, end_offset)) < 0) | 
|  | 362 | return -1; | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 363 | LOG(INFO) << ret << " payload bytes written"; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 364 | written += ret; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 365 | } | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 366 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 367 | LOG(INFO) << "response generation complete, " << written | 
|  | 368 | << " total bytes written"; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 369 | return written; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 370 | } | 
|  | 371 |  | 
| Gilad Arnold | e4ad250 | 2011-12-29 17:08:54 -0800 | [diff] [blame] | 372 | ssize_t HandleGet(int fd, const HttpRequest& request, | 
|  | 373 | const size_t total_length) { | 
|  | 374 | return HandleGet(fd, request, total_length, 0, 0, 0); | 
|  | 375 | } | 
|  | 376 |  | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 377 | // Handles /redirect/<code>/<url> requests by returning the specified | 
|  | 378 | // redirect <code> with a location pointing to /<url>. | 
|  | 379 | void HandleRedirect(int fd, const HttpRequest& request) { | 
|  | 380 | LOG(INFO) << "Redirecting..."; | 
|  | 381 | string url = request.url; | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 382 | CHECK_EQ(static_cast<size_t>(0), url.find("/redirect/")); | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 383 | url.erase(0, strlen("/redirect/")); | 
|  | 384 | string::size_type url_start = url.find('/'); | 
|  | 385 | CHECK_NE(url_start, string::npos); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 386 | HttpResponseCode code = StringToHttpResponseCode(url.c_str()); | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 387 | url.erase(0, url_start); | 
|  | 388 | url = "http://" + request.host + url; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 389 | const char *status = GetHttpResponseDescription(code); | 
|  | 390 | if (!status) | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 391 | CHECK(false) << "Unrecognized redirection code: " << code; | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 392 | LOG(INFO) << "Code: " << code << " " << status; | 
|  | 393 | LOG(INFO) << "New URL: " << url; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 394 |  | 
|  | 395 | ssize_t ret; | 
|  | 396 | if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " + | 
|  | 397 | status + EOL)) < 0) | 
|  | 398 | return; | 
|  | 399 | WriteString(fd, "Location: " + url + EOL); | 
|  | 400 | } | 
|  | 401 |  | 
|  | 402 | // Generate a page not found error response with actual text payload. Return | 
|  | 403 | // number of bytes written or -1 for error. | 
|  | 404 | ssize_t HandleError(int fd, const HttpRequest& request) { | 
|  | 405 | LOG(INFO) << "Generating error HTTP response"; | 
|  | 406 |  | 
|  | 407 | ssize_t ret; | 
|  | 408 | size_t written = 0; | 
|  | 409 |  | 
|  | 410 | const string data("This is an error page."); | 
|  | 411 |  | 
|  | 412 | if ((ret = WriteHeaders(fd, 0, data.size(), kHttpResponseNotFound)) < 0) | 
|  | 413 | return -1; | 
|  | 414 | written += ret; | 
|  | 415 |  | 
|  | 416 | if ((ret = WriteString(fd, data)) < 0) | 
|  | 417 | return -1; | 
|  | 418 | written += ret; | 
|  | 419 |  | 
|  | 420 | return written; | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | // Generate an error response if the requested offset is nonzero, up to a given | 
|  | 424 | // maximal number of successive failures.  The error generated is an "Internal | 
|  | 425 | // Server Error" (500). | 
|  | 426 | ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request, | 
|  | 427 | size_t end_offset, int max_fails) { | 
|  | 428 | static int num_fails = 0; | 
|  | 429 |  | 
|  | 430 | if (request.start_offset > 0 && num_fails < max_fails) { | 
|  | 431 | LOG(INFO) << "Generating error HTTP response"; | 
|  | 432 |  | 
|  | 433 | ssize_t ret; | 
|  | 434 | size_t written = 0; | 
|  | 435 |  | 
|  | 436 | const string data("This is an error page."); | 
|  | 437 |  | 
|  | 438 | if ((ret = WriteHeaders(fd, 0, data.size(), | 
|  | 439 | kHttpResponseInternalServerError)) < 0) | 
|  | 440 | return -1; | 
|  | 441 | written += ret; | 
|  | 442 |  | 
|  | 443 | if ((ret = WriteString(fd, data)) < 0) | 
|  | 444 | return -1; | 
|  | 445 | written += ret; | 
|  | 446 |  | 
|  | 447 | num_fails++; | 
|  | 448 | return written; | 
|  | 449 | } else { | 
|  | 450 | num_fails = 0; | 
|  | 451 | return HandleGet(fd, request, end_offset); | 
|  | 452 | } | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 453 | } | 
|  | 454 |  | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 455 | // Returns a valid response echoing in the body of the response all the headers | 
|  | 456 | // sent by the client. | 
|  | 457 | void HandleEchoHeaders(int fd, const HttpRequest& request) { | 
|  | 458 | WriteHeaders(fd, 0, request.raw_headers.size(), kHttpResponseOk); | 
|  | 459 | WriteString(fd, request.raw_headers); | 
|  | 460 | } | 
|  | 461 |  | 
| Alex Deymo | f123ae2 | 2015-09-24 14:59:43 -0700 | [diff] [blame] | 462 | void HandleHang(int fd) { | 
|  | 463 | LOG(INFO) << "Hanging until the other side of the connection is closed."; | 
|  | 464 | char c; | 
|  | 465 | while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {} | 
|  | 466 | } | 
|  | 467 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 468 | void HandleDefault(int fd, const HttpRequest& request) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 469 | const off_t start_offset = request.start_offset; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 470 | const string data("unhandled path"); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 471 | const size_t size = data.size(); | 
|  | 472 | ssize_t ret; | 
|  | 473 |  | 
|  | 474 | if ((ret = WriteHeaders(fd, start_offset, size, request.return_code)) < 0) | 
|  | 475 | return; | 
|  | 476 | WriteString(fd, (start_offset < static_cast<off_t>(size) ? | 
|  | 477 | data.substr(start_offset) : "")); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 478 | } | 
|  | 479 |  | 
| Gilad Arnold | 48085ba | 2011-11-16 09:36:08 -0800 | [diff] [blame] | 480 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 481 | // Break a URL into terms delimited by slashes. | 
|  | 482 | class UrlTerms { | 
|  | 483 | public: | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 484 | UrlTerms(const string &url, size_t num_terms) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 485 | // URL must be non-empty and start with a slash. | 
| Mike Frysinger | 0f9547d | 2012-02-16 12:11:37 -0500 | [diff] [blame] | 486 | CHECK_GT(url.size(), static_cast<size_t>(0)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 487 | CHECK_EQ(url[0], '/'); | 
|  | 488 |  | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 489 | // Split it into terms delimited by slashes, omitting the preceding slash. | 
|  | 490 | terms = base::SplitString(url.substr(1), "/", base::KEEP_WHITESPACE, | 
|  | 491 | base::SPLIT_WANT_ALL); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 492 |  | 
|  | 493 | // Ensure expected length. | 
|  | 494 | CHECK_EQ(terms.size(), num_terms); | 
|  | 495 | } | 
|  | 496 |  | 
|  | 497 | inline string Get(const off_t index) const { | 
|  | 498 | return terms[index]; | 
|  | 499 | } | 
|  | 500 | inline const char *GetCStr(const off_t index) const { | 
|  | 501 | return Get(index).c_str(); | 
|  | 502 | } | 
|  | 503 | inline int GetInt(const off_t index) const { | 
|  | 504 | return atoi(GetCStr(index)); | 
|  | 505 | } | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 506 | inline size_t GetSizeT(const off_t index) const { | 
|  | 507 | return static_cast<size_t>(atol(GetCStr(index))); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 508 | } | 
|  | 509 |  | 
|  | 510 | private: | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 511 | vector<string> terms; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 512 | }; | 
| Gilad Arnold | 48085ba | 2011-11-16 09:36:08 -0800 | [diff] [blame] | 513 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 514 | void HandleConnection(int fd) { | 
|  | 515 | HttpRequest request; | 
|  | 516 | ParseRequest(fd, &request); | 
|  | 517 |  | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 518 | string &url = request.url; | 
| Jay Srinivasan | 135a58b | 2012-07-13 12:46:49 -0700 | [diff] [blame] | 519 | LOG(INFO) << "pid(" << getpid() <<  "): handling url " << url; | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 520 | if (url == "/quitquitquit") { | 
|  | 521 | HandleQuit(fd); | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 522 | } else if (base::StartsWith( | 
|  | 523 | url, "/download/", base::CompareCase::SENSITIVE)) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 524 | const UrlTerms terms(url, 2); | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 525 | HandleGet(fd, request, terms.GetSizeT(1)); | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 526 | } else if (base::StartsWith(url, "/flaky/", base::CompareCase::SENSITIVE)) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 527 | const UrlTerms terms(url, 5); | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 528 | HandleGet(fd, request, terms.GetSizeT(1), terms.GetSizeT(2), | 
|  | 529 | terms.GetInt(3), terms.GetInt(4)); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 530 | } else if (url.find("/redirect/") == 0) { | 
| Darin Petkov | 41c2fcf | 2010-08-25 13:14:48 -0700 | [diff] [blame] | 531 | HandleRedirect(fd, request); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 532 | } else if (url == "/error") { | 
| Gilad Arnold | 48085ba | 2011-11-16 09:36:08 -0800 | [diff] [blame] | 533 | HandleError(fd, request); | 
| Alex Vakulenko | a3cf75a | 2016-01-20 07:56:15 -0800 | [diff] [blame] | 534 | } else if (base::StartsWith(url, "/error-if-offset/", | 
|  | 535 | base::CompareCase::SENSITIVE)) { | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 536 | const UrlTerms terms(url, 3); | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 537 | HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2)); | 
| Alex Deymo | 6f10c5f | 2016-03-03 22:35:43 -0800 | [diff] [blame] | 538 | } else if (url == "/echo-headers") { | 
|  | 539 | HandleEchoHeaders(fd, request); | 
| Alex Deymo | f123ae2 | 2015-09-24 14:59:43 -0700 | [diff] [blame] | 540 | } else if (url == "/hang") { | 
|  | 541 | HandleHang(fd); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 542 | } else { | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 543 | HandleDefault(fd, request); | 
| Gilad Arnold | 9bedeb5 | 2011-11-17 16:19:57 -0800 | [diff] [blame] | 544 | } | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 545 |  | 
|  | 546 | close(fd); | 
|  | 547 | } | 
|  | 548 |  | 
|  | 549 | }  // namespace chromeos_update_engine | 
|  | 550 |  | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 551 | using namespace chromeos_update_engine;  // NOLINT(build/namespaces) | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 552 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 553 |  | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 554 | void usage(const char *prog_arg) { | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 555 | fprintf( | 
|  | 556 | stderr, | 
|  | 557 | "Usage: %s [ FILE ]\n" | 
|  | 558 | "Once accepting connections, the following is written to FILE (or " | 
|  | 559 | "stdout):\n" | 
|  | 560 | "\"%sN\" (where N is an integer port number)\n", | 
|  | 561 | basename(prog_arg), kListeningMsgPrefix); | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 562 | } | 
|  | 563 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 564 | int main(int argc, char** argv) { | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 565 | // Check invocation. | 
|  | 566 | if (argc > 2) | 
|  | 567 | errx(RC_BAD_ARGS, "unexpected number of arguments (use -h for usage)"); | 
|  | 568 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 569 | // Parse (optional) argument. | 
|  | 570 | int report_fd = STDOUT_FILENO; | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 571 | if (argc == 2) { | 
|  | 572 | if (!strcmp(argv[1], "-h")) { | 
|  | 573 | usage(argv[0]); | 
|  | 574 | exit(RC_OK); | 
|  | 575 | } | 
|  | 576 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 577 | report_fd = open(argv[1], O_WRONLY | O_CREAT, 00644); | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 578 | } | 
|  | 579 |  | 
| Andrew de los Reyes | 3fd5d30 | 2010-10-07 20:07:18 -0700 | [diff] [blame] | 580 | // Ignore SIGPIPE on write() to sockets. | 
|  | 581 | signal(SIGPIPE, SIG_IGN); | 
| Darin Petkov | f67bb1f | 2010-11-08 16:10:26 -0800 | [diff] [blame] | 582 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 583 | int listen_fd = socket(AF_INET, SOCK_STREAM, 0); | 
|  | 584 | if (listen_fd < 0) | 
|  | 585 | LOG(FATAL) << "socket() failed"; | 
|  | 586 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 587 | struct sockaddr_in server_addr = sockaddr_in(); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 588 | server_addr.sin_family = AF_INET; | 
|  | 589 | server_addr.sin_addr.s_addr = INADDR_ANY; | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 590 | server_addr.sin_port = 0; | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 591 |  | 
|  | 592 | { | 
|  | 593 | // Get rid of "Address in use" error | 
|  | 594 | int tr = 1; | 
|  | 595 | if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr, | 
|  | 596 | sizeof(int)) == -1) { | 
|  | 597 | perror("setsockopt"); | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 598 | exit(RC_ERR_SETSOCKOPT); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 599 | } | 
|  | 600 | } | 
|  | 601 |  | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 602 | // Bind the socket and set for listening. | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 603 | if (bind(listen_fd, reinterpret_cast<struct sockaddr *>(&server_addr), | 
|  | 604 | sizeof(server_addr)) < 0) { | 
|  | 605 | perror("bind"); | 
| Gilad Arnold | 97bdb3f | 2013-06-30 23:05:50 -0700 | [diff] [blame] | 606 | exit(RC_ERR_BIND); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 607 | } | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 608 | if (listen(listen_fd, 5) < 0) { | 
|  | 609 | perror("listen"); | 
|  | 610 | exit(RC_ERR_LISTEN); | 
|  | 611 | } | 
|  | 612 |  | 
|  | 613 | // Check the actual port. | 
|  | 614 | struct sockaddr_in bound_addr = sockaddr_in(); | 
|  | 615 | socklen_t bound_addr_len = sizeof(bound_addr); | 
|  | 616 | if (getsockname(listen_fd, reinterpret_cast<struct sockaddr*>(&bound_addr), | 
|  | 617 | &bound_addr_len) < 0) { | 
|  | 618 | perror("getsockname"); | 
|  | 619 | exit(RC_ERR_GETSOCKNAME); | 
|  | 620 | } | 
|  | 621 | in_port_t port = ntohs(bound_addr.sin_port); | 
|  | 622 |  | 
|  | 623 | // Output the listening port, indicating that the server is processing | 
|  | 624 | // requests. IMPORTANT! (a) the format of this message is as expected by some | 
|  | 625 | // unit tests, avoid unilateral changes; (b) it is necessary to flush/sync the | 
|  | 626 | // file to prevent the spawning process from waiting indefinitely for this | 
|  | 627 | // message. | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 628 | string listening_msg = base::StringPrintf("%s%hu", kListeningMsgPrefix, port); | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 629 | LOG(INFO) << listening_msg; | 
|  | 630 | CHECK_EQ(write(report_fd, listening_msg.c_str(), listening_msg.length()), | 
|  | 631 | static_cast<int>(listening_msg.length())); | 
|  | 632 | CHECK_EQ(write(report_fd, "\n", 1), 1); | 
|  | 633 | if (report_fd == STDOUT_FILENO) | 
|  | 634 | fsync(report_fd); | 
|  | 635 | else | 
|  | 636 | close(report_fd); | 
|  | 637 |  | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 638 | while (1) { | 
| Gilad Arnold | b6c562a | 2013-07-01 02:19:26 -0700 | [diff] [blame] | 639 | LOG(INFO) << "pid(" << getpid() <<  "): waiting to accept new connection"; | 
| Alex Vakulenko | 88b591f | 2014-08-28 16:48:57 -0700 | [diff] [blame] | 640 | int client_fd = accept(listen_fd, nullptr, nullptr); | 
| adlr@google.com | 3defe6a | 2009-12-04 20:57:17 +0000 | [diff] [blame] | 641 | LOG(INFO) << "got past accept"; | 
|  | 642 | if (client_fd < 0) | 
|  | 643 | LOG(FATAL) << "ERROR on accept"; | 
|  | 644 | HandleConnection(client_fd); | 
|  | 645 | } | 
|  | 646 | return 0; | 
|  | 647 | } |