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