1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
6
7#include "base/strings/string_util.h"
8#include "mojo/public/cpp/system/data_pipe.h"
9#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h"
10#include "third_party/WebKit/public/platform/WebURLRequest.h"
11
12namespace mojo {
13namespace {
14
15// Ripped from web_url_loader_impl.cc.
16class HeaderFlattener : public blink::WebHTTPHeaderVisitor {
17 public:
18  HeaderFlattener() : has_accept_header_(false) {}
19
20  virtual void visitHeader(const blink::WebString& name,
21                           const blink::WebString& value) {
22    // Headers are latin1.
23    const std::string& name_latin1 = name.latin1();
24    const std::string& value_latin1 = value.latin1();
25
26    // Skip over referrer headers found in the header map because we already
27    // pulled it out as a separate parameter.
28    if (LowerCaseEqualsASCII(name_latin1, "referer"))
29      return;
30
31    if (LowerCaseEqualsASCII(name_latin1, "accept"))
32      has_accept_header_ = true;
33
34    buffer_.push_back(name_latin1 + ": " + value_latin1);
35  }
36
37  Array<String> GetBuffer() {
38    // In some cases, WebKit doesn't add an Accept header, but not having the
39    // header confuses some web servers.  See bug 808613.
40    if (!has_accept_header_) {
41      buffer_.push_back("Accept: */*");
42      has_accept_header_ = true;
43    }
44    return buffer_.Pass();
45  }
46
47 private:
48  Array<String> buffer_;
49  bool has_accept_header_;
50};
51
52void AddRequestBody(URLRequest* url_request,
53                    const blink::WebURLRequest& request) {
54  if (request.httpBody().isNull())
55    return;
56
57  uint32_t i = 0;
58  blink::WebHTTPBody::Element element;
59  while (request.httpBody().elementAt(i++, element)) {
60    switch (element.type) {
61      case blink::WebHTTPBody::Element::TypeData:
62        if (!element.data.isEmpty()) {
63          // WebKit sometimes gives up empty data to append. These aren't
64          // necessary so we just optimize those out here.
65          uint32_t num_bytes = static_cast<uint32_t>(element.data.size());
66          MojoCreateDataPipeOptions options;
67          options.struct_size = sizeof(MojoCreateDataPipeOptions);
68          options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
69          options.element_num_bytes = 1;
70          options.capacity_num_bytes = num_bytes;
71          DataPipe data_pipe(options);
72          url_request->body.push_back(
73              data_pipe.consumer_handle.Pass());
74          WriteDataRaw(data_pipe.producer_handle.get(),
75                       element.data.data(),
76                       &num_bytes,
77                       MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
78        }
79        break;
80      case blink::WebHTTPBody::Element::TypeFile:
81      case blink::WebHTTPBody::Element::TypeFileSystemURL:
82      case blink::WebHTTPBody::Element::TypeBlob:
83        // TODO(mpcomplete): handle these.
84        NOTIMPLEMENTED();
85        break;
86      default:
87        NOTREACHED();
88    }
89  }
90}
91
92} // namespace
93
94URLRequestPtr TypeConverter<URLRequestPtr, blink::WebURLRequest>::Convert(
95    const blink::WebURLRequest& request) {
96  URLRequestPtr url_request(URLRequest::New());
97  url_request->url = request.url().string().utf8();
98  url_request->method = request.httpMethod().utf8();
99
100  HeaderFlattener flattener;
101  request.visitHTTPHeaderFields(&flattener);
102  url_request->headers = flattener.GetBuffer().Pass();
103
104  AddRequestBody(url_request.get(), request);
105
106  return url_request.Pass();
107}
108
109}  // namespace mojo
110
111