blob: 1931547e21f80cd4bc5b4847a207ddb2753ccd72 [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
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -07008
9#include <base/bind.h>
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070010#include <event2/bufferevent.h>
11#include <event2/buffer.h>
12#include <event2/http.h>
ilewisf1fa93d2015-11-09 09:01:11 -080013#include <weave/enum_to_string.h>
14
15#include "examples/provider/event_deleter.h"
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070016
17// EventHttpClient based on libevent2 http-client sample
18// TODO(proppy): https
19// TODO(proppy): hostname validation
20namespace weave {
21
22namespace {
23const weave::EnumToStringMap<evhttp_cmd_type>::Map kMapMethod[] = {
24 {EVHTTP_REQ_GET, "GET"}, {EVHTTP_REQ_POST, "POST"},
25 {EVHTTP_REQ_HEAD, "HEAD"}, {EVHTTP_REQ_PUT, "PUT"},
26 {EVHTTP_REQ_PATCH, "PATCH"}, {EVHTTP_REQ_DELETE, "DELETE"},
27 {EVHTTP_REQ_OPTIONS, "OPTIONS"}};
28} // namespace
29
30template <>
31EnumToStringMap<evhttp_cmd_type>::EnumToStringMap()
32 : EnumToStringMap(kMapMethod) {}
33
34using namespace provider;
35
36namespace examples {
37
38namespace {
39
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070040class EventHttpResponse : public weave::provider::HttpClient::Response {
41 public:
Vitaly Buka866b60a2015-10-09 14:24:55 -070042 int GetStatusCode() const override { return status; }
43 std::string GetContentType() const override { return content_type; }
Vitaly Buka4774df22015-10-09 12:36:22 -070044 std::string GetData() const { return data; }
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070045
46 int status;
47 std::string content_type;
48 std::string data;
49};
50
51struct EventRequestState {
52 TaskRunner* task_runner_;
ilewisf1fa93d2015-11-09 09:01:11 -080053 EventPtr<evhttp_uri> http_uri_;
54 EventPtr<evhttp_connection> evcon_;
Vitaly Buka74763422015-10-11 00:39:52 -070055 HttpClient::SendRequestCallback callback_;
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070056};
57
58void RequestDoneCallback(evhttp_request* req, void* ctx) {
59 std::unique_ptr<EventRequestState> state{
60 static_cast<EventRequestState*>(ctx)};
61 if (!req) {
62 ErrorPtr error;
63 auto err = EVUTIL_SOCKET_ERROR();
64 Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
65 "request failed: %s",
66 evutil_socket_error_to_string(err));
67 state->task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -070068 FROM_HERE, base::Bind(state->callback_, nullptr, base::Passed(&error)),
Vitaly Bukaf7f52d42015-10-10 22:43:55 -070069 {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070070 return;
71 }
72 std::unique_ptr<EventHttpResponse> response{new EventHttpResponse()};
73 response->status = evhttp_request_get_response_code(req);
74 auto buffer = evhttp_request_get_input_buffer(req);
75 auto length = evbuffer_get_length(buffer);
76 response->data.resize(length);
77 auto n = evbuffer_remove(buffer, &response->data[0], length);
78 CHECK_EQ(n, int(length));
79 state->task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -070080 FROM_HERE, base::Bind(state->callback_, base::Passed(&response), nullptr),
81 {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070082}
83
84} // namespace
85
86EventHttpClient::EventHttpClient(EventTaskRunner* task_runner)
87 : task_runner_{task_runner} {}
88
Vitaly Buka1a42e142015-10-10 18:15:15 -070089void EventHttpClient::SendRequest(Method method,
Vitaly Buka866b60a2015-10-09 14:24:55 -070090 const std::string& url,
91 const Headers& headers,
92 const std::string& data,
Vitaly Buka74763422015-10-11 00:39:52 -070093 const SendRequestCallback& callback) {
Vitaly Buka9e9aca92015-11-23 23:20:12 -080094 evhttp_cmd_type method_id = EVHTTP_REQ_GET;
Vitaly Buka1a42e142015-10-10 18:15:15 -070095 CHECK(weave::StringToEnum(weave::EnumToString(method), &method_id));
ilewisf1fa93d2015-11-09 09:01:11 -080096 EventPtr<evhttp_uri> http_uri{evhttp_uri_parse(url.c_str())};
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -070097 CHECK(http_uri);
98 auto host = evhttp_uri_get_host(http_uri.get());
99 CHECK(host);
100 auto port = evhttp_uri_get_port(http_uri.get());
101 if (port == -1)
102 port = 80;
103 std::string path{evhttp_uri_get_path(http_uri.get())};
104 if (path.length() == 0) {
105 path = "/";
106 }
107 std::string uri{path};
108 auto query = evhttp_uri_get_query(http_uri.get());
109 if (query) {
110 uri = path + "?" + query;
111 }
112 auto bev = bufferevent_socket_new(task_runner_->GetEventBase(), -1,
113 BEV_OPT_CLOSE_ON_FREE);
114 CHECK(bev);
ilewisf1fa93d2015-11-09 09:01:11 -0800115 EventPtr<evhttp_connection> conn{evhttp_connection_base_bufferevent_new(
116 task_runner_->GetEventBase(), NULL, bev, host, port)};
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700117 CHECK(conn);
ilewisf1fa93d2015-11-09 09:01:11 -0800118 EventPtr<evhttp_request> req{evhttp_request_new(
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700119 &RequestDoneCallback,
Vitaly Buka866b60a2015-10-09 14:24:55 -0700120 new EventRequestState{task_runner_, std::move(http_uri), std::move(conn),
Vitaly Buka74763422015-10-11 00:39:52 -0700121 callback})};
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700122 CHECK(req);
123 auto output_headers = evhttp_request_get_output_headers(req.get());
124 evhttp_add_header(output_headers, "Host", host);
125 for (auto& kv : headers)
126 evhttp_add_header(output_headers, kv.first.c_str(), kv.second.c_str());
127 if (!data.empty()) {
128 auto output_buffer = evhttp_request_get_output_buffer(req.get());
129 evbuffer_add(output_buffer, data.c_str(), data.length());
130 evhttp_add_header(output_headers, "Content-Length",
131 std::to_string(data.length()).c_str());
132 }
133 auto res =
134 evhttp_make_request(conn.get(), req.release(), method_id, uri.c_str());
Vitaly Buka866b60a2015-10-09 14:24:55 -0700135 if (res >= 0)
136 return;
137 ErrorPtr error;
138 Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
Vitaly Buka1a42e142015-10-10 18:15:15 -0700139 "request failed: %s %s", EnumToString(method).c_str(),
140 url.c_str());
Vitaly Bukaf7f52d42015-10-10 22:43:55 -0700141 task_runner_->PostDelayedTask(
Vitaly Buka74763422015-10-11 00:39:52 -0700142 FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
Johan Euphrosine0ef2cd02015-10-02 15:24:50 -0700143}
144
145} // namespace examples
146} // namespace weave