| 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;
 | 
| +}
 | 
| 
 |