Coverage for /Syzygy/kasko/upload.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
94.2%971030.C++source

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "base/logging.h"
  16    :  #include "base/strings/string_util.h"
  17    :  #include "base/strings/utf_string_conversions.h"
  18    :  
  19    :  #include "syzygy/common/com_utils.h"
  20    :  #include "syzygy/kasko/http_agent.h"
  21    :  #include "syzygy/kasko/http_response.h"
  22    :  #include "syzygy/kasko/internet_helpers.h"
  23    :  
  24    :  namespace kasko {
  25    :  
  26    :  namespace {
  27    :  
  28    :  // Reads up to |count| bytes of raw response body into |buffer|. Returns true if
  29    :  // the entire response body is successfully read. Regardless of success or
  30    :  // failure, |*count| will be assigned the number of bytes read (between 0 and
  31    :  // the original value of |*count|).
  32  E :  bool ReadResponseData(HttpResponse *response, char* buffer, size_t* count) {
  33  E :    size_t content_length_header_value = 0;
  34  E :    bool has_content_length_header = false;
  35    :  
  36  E :    size_t buffer_size = *count;
  37  E :    *count = 0;
  38    :  
  39    :    if (!response->GetContentLength(&has_content_length_header,
  40  E :                                    &content_length_header_value)) {
  41  i :      return false;
  42    :    }
  43    :  
  44    :    do {
  45  E :      size_t single_read_count = buffer_size - *count;
  46  E :      if (!response->ReadData(buffer + *count, &single_read_count))
  47  E :        return false;
  48    :  
  49  E :      if (single_read_count == 0)
  50  E :        break;
  51    :  
  52  E :      *count += single_read_count;
  53  E :    } while (*count < buffer_size);
  54    :  
  55  E :    bool has_more_data = false;
  56  E :    if (!response->HasData(&has_more_data))
  57  i :      return false;
  58  E :    if (has_more_data) {
  59  E :      LOG(ERROR) << "Incoming data exceeds anticipated maximum of " << *count
  60    :                 << " bytes.";
  61  E :      return false;
  62    :    }
  63    :  
  64    :    if (has_content_length_header &&
  65  E :        (*count != content_length_header_value)) {
  66  E :      LOG(ERROR) << "Response body length of " << *count
  67    :                 << " differs from content length header value "
  68    :                 << content_length_header_value;
  69  E :      return false;
  70    :    }
  71    :  
  72  E :    return true;
  73  E :  }
  74    :  
  75    :  // Reads and parses the Content-Type header from |response| and stores the
  76    :  // resulting character set and MIME type in |charset| and |mime_type|. |charset|
  77    :  // and |mime_type| will be empty if they are not present. Returns true if the
  78    :  // header is absent or present and successfully parsed.
  79    :  bool GetCharsetAndMimeType(HttpResponse* response,
  80    :                             base::string16* charset,
  81  E :                             base::string16* mime_type) {
  82  E :    base::string16 content_type;
  83  E :    bool has_content_type = false;
  84  E :    if (!response->GetContentType(&has_content_type, &content_type))
  85  i :      return false;
  86    :  
  87  E :    if (!has_content_type) {
  88  E :      charset->clear();
  89  E :      mime_type->clear();
  90  E :      return true;
  91    :    }
  92    :  
  93  E :    bool had_charset = false;
  94  E :    base::string16 boundary;
  95  E :    ParseContentType(content_type, mime_type, charset, &had_charset, &boundary);
  96  E :    return true;
  97  E :  }
  98    :  
  99    :  // Reads the response body and stores it in |response_body|. Does character set
 100    :  // conversion if necessary. Returns true if the entire response body is
 101    :  // successfully read. Regardless of success or failure, |*response_body| will
 102    :  // contain a best effort interpretation of the partially or fully read response
 103    :  // body.
 104  E :  bool ReadResponse(HttpResponse* response, base::string16 *response_body) {
 105  E :    DCHECK(response);
 106  E :    DCHECK(response_body);
 107    :  
 108    :    // We are only expecting a small identifier string.
 109    :    char buffer[256];
 110  E :    size_t total_read = sizeof(buffer);
 111  E :    if (!ReadResponseData(response, buffer, &total_read)) {
 112    :      // Interpret the partial body (if any) as ASCII for diagnostic output.
 113  E :      *response_body = base::string16(buffer, buffer + total_read);
 114  E :      return false;
 115    :    }
 116    :  
 117  E :    base::string16 charset, mime_type;
 118  E :    if (!GetCharsetAndMimeType(response, &charset, &mime_type)) {
 119    :      // Interpret the body as ASCII for diagnostic output.
 120  i :      *response_body = base::string16(buffer, buffer + total_read);
 121  i :      return false;
 122    :    }
 123    :  
 124  E :    if (charset.empty() || charset == L"utf-8") {
 125  E :      *response_body = base::UTF8ToUTF16(base::StringPiece(buffer, total_read));
 126  E :    } else if (charset == L"utf-16") {
 127    :      *response_body =
 128    :          base::string16(reinterpret_cast<const base::char16*>(buffer),
 129  E :                         total_read / sizeof(base::char16));
 130  E :    } else if (charset == L"iso-8859-1" &&
 131  E :               base::IsStringASCII(base::StringPiece(buffer, total_read))) {
 132    :      // Although labeled as latin-1, this string is also valid ASCII.
 133  E :      *response_body = base::ASCIIToUTF16(base::StringPiece(buffer, total_read));
 134  E :    } else {
 135  E :      LOG(ERROR) << "Unexpected charset: " << charset;
 136    :      // Interpret the body as ASCII for diagnostic output.
 137  E :      *response_body = base::string16(buffer, buffer + total_read);
 138  E :      return false;
 139    :    }
 140    :  
 141  E :    if (!mime_type.empty() && mime_type != L"text/plain") {
 142    :      // If the body is labeled text/html but is clearly not HTML we will treat it
 143    :      // as text/plain.
 144    :      if (mime_type != L"text/html" ||
 145  E :          response_body->find_first_of(L"<>") != base::string16::npos) {
 146  E :          LOG(ERROR) << "Unexpected MIME type: " << mime_type;
 147  E :          return false;
 148    :      }
 149    :    }
 150    :  
 151  E :    return true;
 152  E :  }
 153    :  
 154    :  }  // namespace
 155    :  
 156    :  bool SendHttpUpload(HttpAgent* agent,
 157    :                      const base::string16& url,
 158    :                      const std::map<base::string16, base::string16>& parameters,
 159    :                      const std::string& upload_file,
 160    :                      const base::string16& file_part_name,
 161    :                      base::string16* response_body,
 162  E :                      uint16_t* response_code) {
 163  E :    DCHECK(response_body);
 164  E :    DCHECK(response_code);
 165    :  
 166  E :    base::string16 scheme, host, path;
 167  E :    uint16_t port = 0;
 168  E :    if (!DecomposeUrl(url, &scheme, &host, &port, &path)) {
 169  E :      LOG(ERROR) << "Failed to decompose URL: " << url;
 170  E :      return false;
 171    :    }
 172    :  
 173  E :    bool secure = false;
 174  E :    if (scheme == L"https") {
 175  E :      secure = true;
 176  E :    } else if (scheme != L"http") {
 177  E :      LOG(ERROR) << "Invalid scheme in URL: " << url;
 178  E :      return false;
 179    :    }
 180    :  
 181  E :    base::string16 boundary = GenerateMultipartHttpRequestBoundary();
 182    :    base::string16 content_type_header =
 183  E :        GenerateMultipartHttpRequestContentTypeHeader(boundary);
 184    :  
 185    :    std::string request_body = GenerateMultipartHttpRequestBody(
 186  E :        parameters, upload_file, file_part_name, boundary);
 187    :  
 188    :    scoped_ptr<HttpResponse> response =
 189  E :        agent->Post(host, port, path, secure, content_type_header, request_body);
 190  E :    if (!response) {
 191  E :      LOG(ERROR) << "Request to " << url << " failed.";
 192  E :      return false;
 193    :    }
 194    :  
 195  E :    uint16_t status_code = 0;
 196  E :    if (!response->GetStatusCode(&status_code))
 197  E :      return false;
 198    :  
 199  E :    *response_code = status_code;
 200    :  
 201  E :    if (status_code != 200) {
 202  E :      LOG(ERROR) << "Request to " << url << " failed with HTTP status code "
 203    :                 << status_code;
 204  E :      return false;
 205    :    }
 206    :  
 207  E :    if (!ReadResponse(response.get(), response_body)) {
 208  E :      if (response_body->length()) {
 209  E :        LOG(ERROR) << "Failure while reading response body. Possibly truncated "
 210    :                      "response body: " << *response_body;
 211  E :      } else {
 212  i :        LOG(ERROR) << "Failure while reading response body.";
 213    :      }
 214  E :      return false;
 215    :    }
 216    :  
 217  E :    return true;
 218  E :  }
 219    :  
 220    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.