blob: b38bd557921b862ab373717b4dc3069d5bd323e0 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Johan Euphrosine3523fdd2015-10-14 20:46:05 -07005#include "examples/provider/event_http_client.h"
6#include "examples/provider/event_task_runner.h"
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -07007
8#include <weave/enum_to_string.h>
9
10#include <string>
11
12#include <base/bind.h>
13
14#include <event2/bufferevent.h>
15#include <event2/buffer.h>
16#include <event2/http.h>
17
18// EventHttpClient based on libevent2 http-client sample
19// TODO(proppy): https
20// TODO(proppy): hostname validation
21namespace weave {
22
23namespace {
24const weave::EnumToStringMap<evhttp_cmd_type>::Map kMapMethod[] = {
25 {EVHTTP_REQ_GET, "GET"}, {EVHTTP_REQ_POST, "POST"},
26 {EVHTTP_REQ_HEAD, "HEAD"}, {EVHTTP_REQ_PUT, "PUT"},
27 {EVHTTP_REQ_PATCH, "PATCH"}, {EVHTTP_REQ_DELETE, "DELETE"},
28 {EVHTTP_REQ_OPTIONS, "OPTIONS"}};
29} // namespace
30
31template <>
32EnumToStringMap<evhttp_cmd_type>::EnumToStringMap()
33 : EnumToStringMap(kMapMethod) {}
34
35using namespace provider;
36
37namespace examples {
38
39namespace {
40
41class EventDeleter {
42 public:
43 void operator()(evhttp_uri* http_uri) { evhttp_uri_free(http_uri); }
44 void operator()(evhttp_connection* conn) { evhttp_connection_free(conn); }
45 void operator()(evhttp_request* req) { evhttp_request_free(req); }
46};
47
48class EventHttpResponse : public weave::provider::HttpClient::Response {
49 public:
Vitaly Buka866b60a2015-10-09 14:24:55 -070050 int GetStatusCode() const override { return status; }
51 std::string GetContentType() const override { return content_type; }
Vitaly Buka4774df22015-10-09 12:36:22 -070052 std::string GetData() const { return data; }
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070053
54 int status;
55 std::string content_type;
56 std::string data;
57};
58
59struct EventRequestState {
60 TaskRunner* task_runner_;
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070061 std::unique_ptr<evhttp_uri, EventDeleter> http_uri_;
62 std::unique_ptr<evhttp_connection, EventDeleter> evcon_;
Vitaly Buka74763422015-10-11 00:39:52 -070063 HttpClient::SendRequestCallback callback_;
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070064};
65
66void RequestDoneCallback(evhttp_request* req, void* ctx) {
67 std::unique_ptr<EventRequestState> state{
68 static_cast<EventRequestState*>(ctx)};
69 if (!req) {
70 ErrorPtr error;
71 auto err = EVUTIL_SOCKET_ERROR();
72 Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
73 "request failed: %s",
74 evutil_socket_error_to_string(err));
75 state->task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -070076 FROM_HERE, base::Bind(state->callback_, nullptr, base::Passed(&error)),
Vitaly Bukaf7f52d42015-10-10 22:43:55 -070077 {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070078 return;
79 }
80 std::unique_ptr<EventHttpResponse> response{new EventHttpResponse()};
81 response->status = evhttp_request_get_response_code(req);
82 auto buffer = evhttp_request_get_input_buffer(req);
83 auto length = evbuffer_get_length(buffer);
84 response->data.resize(length);
85 auto n = evbuffer_remove(buffer, &response->data[0], length);
86 CHECK_EQ(n, int(length));
87 state->task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -070088 FROM_HERE, base::Bind(state->callback_, base::Passed(&response), nullptr),
89 {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070090}
91
92} // namespace
93
94EventHttpClient::EventHttpClient(EventTaskRunner* task_runner)
95 : task_runner_{task_runner} {}
96
Vitaly Buka1a42e142015-10-10 18:15:15 -070097void EventHttpClient::SendRequest(Method method,
Vitaly Buka866b60a2015-10-09 14:24:55 -070098 const std::string& url,
99 const Headers& headers,
100 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -0700101 const SendRequestCallback& callback) {
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700102 evhttp_cmd_type method_id;
Vitaly Buka1a42e142015-10-10 18:15:15 -0700103 CHECK(weave::StringToEnum(weave::EnumToString(method), &method_id));
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700104 std::unique_ptr<evhttp_uri, EventDeleter> http_uri{
105 evhttp_uri_parse(url.c_str())};
106 CHECK(http_uri);
107 auto host = evhttp_uri_get_host(http_uri.get());
108 CHECK(host);
109 auto port = evhttp_uri_get_port(http_uri.get());
110 if (port == -1)
111 port = 80;
112 std::string path{evhttp_uri_get_path(http_uri.get())};
113 if (path.length() == 0) {
114 path = "/";
115 }
116 std::string uri{path};
117 auto query = evhttp_uri_get_query(http_uri.get());
118 if (query) {
119 uri = path + "?" + query;
120 }
121 auto bev = bufferevent_socket_new(task_runner_->GetEventBase(), -1,
122 BEV_OPT_CLOSE_ON_FREE);
123 CHECK(bev);
124 std::unique_ptr<evhttp_connection, EventDeleter> conn{
125 evhttp_connection_base_bufferevent_new(task_runner_->GetEventBase(), NULL,
126 bev, host, port)};
127 CHECK(conn);
128 std::unique_ptr<evhttp_request, EventDeleter> req{evhttp_request_new(
129 &RequestDoneCallback,
Vitaly Buka866b60a2015-10-09 14:24:55 -0700130 new EventRequestState{task_runner_, std::move(http_uri), std::move(conn),
Vitaly Buka74763422015-10-11 00:39:52 -0700131 callback})};
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700132 CHECK(req);
133 auto output_headers = evhttp_request_get_output_headers(req.get());
134 evhttp_add_header(output_headers, "Host", host);
135 for (auto& kv : headers)
136 evhttp_add_header(output_headers, kv.first.c_str(), kv.second.c_str());
137 if (!data.empty()) {
138 auto output_buffer = evhttp_request_get_output_buffer(req.get());
139 evbuffer_add(output_buffer, data.c_str(), data.length());
140 evhttp_add_header(output_headers, "Content-Length",
141 std::to_string(data.length()).c_str());
142 }
143 auto res =
144 evhttp_make_request(conn.get(), req.release(), method_id, uri.c_str());
Vitaly Buka866b60a2015-10-09 14:24:55 -0700145 if (res >= 0)
146 return;
147 ErrorPtr error;
148 Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
Vitaly Buka1a42e142015-10-10 18:15:15 -0700149 "request failed: %s %s", EnumToString(method).c_str(),
150 url.c_str());
Vitaly Bukaf7f52d42015-10-10 22:43:55 -0700151 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700152 FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700153}
154
155} // namespace examples
156} // namespace weave