| Index: src/platform/update_engine/test_http_server.cc
|
| diff --git a/src/platform/update_engine/test_http_server.cc b/src/platform/update_engine/test_http_server.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..94c88fa1bb4b9700ff5e14bd7b3a5142dff4cf4f
|
| --- /dev/null
|
| +++ b/src/platform/update_engine/test_http_server.cc
|
| @@ -0,0 +1,240 @@
|
| +// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// This file implements a simple HTTP server. It can exhibit odd behavior
|
| +// that's useful for testing. For example, it's useful to test that
|
| +// the updater can continue a connection if it's dropped, or that it
|
| +// handles very slow data transfers.
|
| +
|
| +// To use this, simply make an HTTP connection to localhost:port and
|
| +// GET a url.
|
| +
|
| +#include <netinet/in.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/types.h>
|
| +#include <errno.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +#include <unistd.h>
|
| +#include <algorithm>
|
| +#include <string>
|
| +#include <vector>
|
| +#include "chromeos/obsolete_logging.h"
|
| +
|
| +using std::min;
|
| +using std::string;
|
| +using std::vector;
|
| +
|
| +namespace chromeos_update_engine {
|
| +
|
| +struct HttpRequest {
|
| + string url;
|
| + off_t offset;
|
| +};
|
| +
|
| +namespace {
|
| +const int kPort = 8080; // hardcoded to 8080 for now
|
| +const int kBigLength = 100000;
|
| +}
|
| +
|
| +bool ParseRequest(int fd, HttpRequest* request) {
|
| + string headers;
|
| + while(headers.find("\r\n\r\n") == string::npos) {
|
| + vector<char> buf(1024);
|
| + memset(&buf[0], 0, buf.size());
|
| + ssize_t r = read(fd, &buf[0], buf.size() - 1);
|
| + if (r < 0) {
|
| + perror("read");
|
| + exit(1);
|
| + }
|
| + buf.resize(r);
|
| +
|
| + headers.insert(headers.end(), buf.begin(), buf.end());
|
| + }
|
| + LOG(INFO) << "got headers: " << headers;
|
| +
|
| + string::size_type url_start, url_end;
|
| + CHECK_NE(headers.find("GET "), string::npos);
|
| + url_start = headers.find("GET ") + strlen("GET ");
|
| + url_end = headers.find(' ', url_start);
|
| + CHECK_NE(string::npos, url_end);
|
| + string url = headers.substr(url_start, url_end - url_start);
|
| + LOG(INFO) << "URL: " << url;
|
| +
|
| + string::size_type range_start, range_end;
|
| + if (headers.find("\r\nRange: ") == string::npos) {
|
| + request->offset = 0;
|
| + } else {
|
| + range_start = headers.find("\r\nRange: ") + strlen("\r\nRange: ");
|
| + range_end = headers.find('\r', range_start);
|
| + CHECK_NE(string::npos, range_end);
|
| + string range_header = headers.substr(range_start, range_end - range_start);
|
| +
|
| + LOG(INFO) << "Range: " << range_header;
|
| + CHECK(*range_header.rbegin() == '-');
|
| + request->offset = atoll(range_header.c_str() + strlen("bytes="));
|
| + LOG(INFO) << "Offset: " << request->offset;
|
| + }
|
| + request->url = url;
|
| + return true;
|
| +}
|
| +
|
| +void WriteString(int fd, const string& str) {
|
| + unsigned int bytes_written = 0;
|
| + while (bytes_written < str.size()) {
|
| + ssize_t r = write(fd, str.c_str() + bytes_written,
|
| + str.size() - bytes_written);
|
| + LOG(INFO) << "write() wrote " << r << " bytes";
|
| + if (r < 0) {
|
| + perror("write");
|
| + return;
|
| + }
|
| + bytes_written += r;
|
| + }
|
| + LOG(INFO) << "WriteString wrote " << bytes_written << " bytes";
|
| +}
|
| +
|
| +string Itoa(off_t num) {
|
| + char buf[100] = {0};
|
| + snprintf(buf, sizeof(buf), "%lld", num);
|
| + return buf;
|
| +}
|
| +
|
| +void WriteHeaders(int fd, bool support_range, off_t full_size,
|
| + off_t start_offset) {
|
| + LOG(INFO) << "writing headers";
|
| + WriteString(fd, "HTTP/1.1 200 OK\r\n");
|
| + WriteString(fd, "Content-Type: application/octet-stream\r\n");
|
| + if (support_range) {
|
| + WriteString(fd, "Accept-Ranges: bytes\r\n");
|
| + WriteString(fd, string("Content-Range: bytes ") + Itoa(start_offset) +
|
| + "-" + Itoa(full_size - 1) + "/" + Itoa(full_size) + "\r\n");
|
| + }
|
| + off_t content_length = full_size;
|
| + if (support_range)
|
| + content_length -= start_offset;
|
| + WriteString(fd, string("Content-Length: ") + Itoa(content_length) + "\r\n");
|
| + WriteString(fd, "\r\n");
|
| +}
|
| +
|
| +void HandleQuitQuitQuit(int fd) {
|
| + exit(0);
|
| +}
|
| +
|
| +void HandleBig(int fd, const HttpRequest& request) {
|
| + const off_t full_length = kBigLength;
|
| + WriteHeaders(fd, true, full_length, request.offset);
|
| + const off_t content_length = full_length - request.offset;
|
| + int i = request.offset;
|
| + for (; i % 10; i++)
|
| + WriteString(fd, string(1, 'a' + (i % 10)));
|
| + CHECK_EQ(i % 10, 0);
|
| + for (; i < content_length; i += 10)
|
| + WriteString(fd, "abcdefghij");
|
| + CHECK_EQ(i, full_length);
|
| +}
|
| +
|
| +// This is like /big, but it writes at most 9000 bytes. Also,
|
| +// half way through it sleeps for 70 seconds
|
| +// (technically, when (offset % (9000 * 7)) == 0).
|
| +void HandleFlaky(int fd, const HttpRequest& request) {
|
| + const off_t full_length = kBigLength;
|
| + WriteHeaders(fd, true, full_length, request.offset);
|
| + const off_t content_length = min(9000LL, full_length - request.offset);
|
| + const bool should_sleep = (request.offset % (9000 * 7)) == 0;
|
| +
|
| + string buf;
|
| +
|
| + for (int i = request.offset; i % 10; i++)
|
| + buf.append(1, 'a' + (i % 10));
|
| + while (buf.size() < content_length)
|
| + buf.append("abcdefghij");
|
| + buf.resize(content_length);
|
| +
|
| + if (!should_sleep) {
|
| + LOG(INFO) << "writing data blob of size " << buf.size();
|
| + WriteString(fd, buf);
|
| + } else {
|
| + string::size_type half_way_point = buf.size() / 2;
|
| + LOG(INFO) << "writing small data blob of size " << half_way_point;
|
| + WriteString(fd, buf.substr(0, half_way_point));
|
| + sleep(10);
|
| + LOG(INFO) << "writing small data blob of size "
|
| + << (buf.size() - half_way_point);
|
| + WriteString(fd, buf.substr(half_way_point, buf.size() - half_way_point));
|
| + }
|
| +}
|
| +
|
| +void HandleDefault(int fd, const HttpRequest& request) {
|
| + const string data("unhandled path");
|
| + WriteHeaders(fd, true, data.size(), request.offset);
|
| + const string data_to_write(data.substr(request.offset,
|
| + data.size() - request.offset));
|
| + WriteString(fd, data_to_write);
|
| +}
|
| +
|
| +void HandleConnection(int fd) {
|
| + HttpRequest request;
|
| + ParseRequest(fd, &request);
|
| +
|
| + if (request.url == "/quitquitquit")
|
| + HandleQuitQuitQuit(fd);
|
| + else if (request.url == "/big")
|
| + HandleBig(fd, request);
|
| + else if (request.url == "/flaky")
|
| + HandleFlaky(fd, request);
|
| + else
|
| + HandleDefault(fd, request);
|
| +
|
| + close(fd);
|
| +}
|
| +
|
| +} // namespace chromeos_update_engine
|
| +
|
| +using namespace chromeos_update_engine;
|
| +
|
| +int main(int argc, char** argv) {
|
| + socklen_t clilen;
|
| + struct sockaddr_in server_addr;
|
| + struct sockaddr_in client_addr;
|
| + memset(&server_addr, 0, sizeof(server_addr));
|
| + memset(&client_addr, 0, sizeof(client_addr));
|
| +
|
| + int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
| + if (listen_fd < 0)
|
| + LOG(FATAL) << "socket() failed";
|
| +
|
| + server_addr.sin_family = AF_INET;
|
| + server_addr.sin_addr.s_addr = INADDR_ANY;
|
| + server_addr.sin_port = htons(kPort);
|
| +
|
| + {
|
| + // Get rid of "Address in use" error
|
| + int tr = 1;
|
| + if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr,
|
| + sizeof(int)) == -1) {
|
| + perror("setsockopt");
|
| + exit(1);
|
| + }
|
| + }
|
| +
|
| + if (bind(listen_fd, reinterpret_cast<struct sockaddr *>(&server_addr),
|
| + sizeof(server_addr)) < 0) {
|
| + perror("bind");
|
| + exit(1);
|
| + }
|
| + CHECK_EQ(listen(listen_fd,5), 0);
|
| + while (1) {
|
| + clilen = sizeof(client_addr);
|
| + int client_fd = accept(listen_fd,
|
| + (struct sockaddr *) &client_addr,
|
| + &clilen);
|
| + LOG(INFO) << "got past accept";
|
| + if (client_fd < 0)
|
| + LOG(FATAL) << "ERROR on accept";
|
| + HandleConnection(client_fd);
|
| + }
|
| + return 0;
|
| +}
|
|
|