blob: 7d2cf21c0fc3ccb2421a8d249d1dc8530549de38 [file] [log] [blame]
David Reiss5ddabb82010-10-06 17:09:37 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
Roger Meier4285ba22013-06-10 21:17:23 +020020#include <thrift/async/TEvhttpServer.h>
21#include <thrift/async/TAsyncBufferProcessor.h>
Roger Meier49ff8b12012-04-13 09:12:31 +000022#include <thrift/transport/TBufferTransports.h>
cyy316723a2019-01-05 16:35:14 +080023#include <memory>
David Reiss5ddabb82010-10-06 17:09:37 +000024#include <evhttp.h>
Ben Craig7207c222015-07-06 08:40:35 -050025#include <event2/buffer.h>
26#include <event2/buffer_compat.h>
Roger Meier08077bf2011-09-11 07:28:54 +000027#include <iostream>
28
Roger Meier7e056e72011-07-17 07:28:28 +000029#ifndef HTTP_INTERNAL // libevent < 2
30#define HTTP_INTERNAL 500
31#endif
32
David Reiss5ddabb82010-10-06 17:09:37 +000033using apache::thrift::transport::TMemoryBuffer;
cyy316723a2019-01-05 16:35:14 +080034using std::shared_ptr;
David Reiss5ddabb82010-10-06 17:09:37 +000035
Konrad Grochowski16a23a62014-11-13 15:33:38 +010036namespace apache {
37namespace thrift {
38namespace async {
David Reiss5ddabb82010-10-06 17:09:37 +000039
40struct TEvhttpServer::RequestContext {
41 struct evhttp_request* req;
cyy316723a2019-01-05 16:35:14 +080042 std::shared_ptr<apache::thrift::transport::TMemoryBuffer> ibuf;
43 std::shared_ptr<apache::thrift::transport::TMemoryBuffer> obuf;
David Reiss5ddabb82010-10-06 17:09:37 +000044
45 RequestContext(struct evhttp_request* req);
46};
47
cyy316723a2019-01-05 16:35:14 +080048TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor)
Sebastian Zenker042580f2019-01-29 15:48:12 +010049 : processor_(processor), eb_(nullptr), eh_(nullptr) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010050}
David Reiss5ddabb82010-10-06 17:09:37 +000051
cyy316723a2019-01-05 16:35:14 +080052TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor, int port)
Sebastian Zenker042580f2019-01-29 15:48:12 +010053 : processor_(processor), eb_(nullptr), eh_(nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +000054 // Create event_base and evhttp.
55 eb_ = event_base_new();
Sebastian Zenker042580f2019-01-29 15:48:12 +010056 if (eb_ == nullptr) {
Roger Meier08077bf2011-09-11 07:28:54 +000057 throw TException("event_base_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000058 }
59 eh_ = evhttp_new(eb_);
Sebastian Zenker042580f2019-01-29 15:48:12 +010060 if (eh_ == nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +000061 event_base_free(eb_);
Roger Meier08077bf2011-09-11 07:28:54 +000062 throw TException("evhttp_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000063 }
64
65 // Bind to port.
Sebastian Zenker042580f2019-01-29 15:48:12 +010066 int ret = evhttp_bind_socket(eh_, nullptr, port);
David Reiss5ddabb82010-10-06 17:09:37 +000067 if (ret < 0) {
68 evhttp_free(eh_);
69 event_base_free(eb_);
Konrad Grochowski16a23a62014-11-13 15:33:38 +010070 throw TException("evhttp_bind_socket failed");
David Reiss5ddabb82010-10-06 17:09:37 +000071 }
72
73 // Register a handler. If you use the other constructor,
74 // you will want to do this yourself.
75 // Don't forget to unregister before destorying this TEvhttpServer.
76 evhttp_set_cb(eh_, "/", request, (void*)this);
77}
78
David Reiss5ddabb82010-10-06 17:09:37 +000079TEvhttpServer::~TEvhttpServer() {
Sebastian Zenker042580f2019-01-29 15:48:12 +010080 if (eh_ != nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +000081 evhttp_free(eh_);
82 }
Sebastian Zenker042580f2019-01-29 15:48:12 +010083 if (eb_ != nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +000084 event_base_free(eb_);
85 }
86}
87
David Reiss5ddabb82010-10-06 17:09:37 +000088int TEvhttpServer::serve() {
Sebastian Zenker042580f2019-01-29 15:48:12 +010089 if (eb_ == nullptr) {
Roger Meier08077bf2011-09-11 07:28:54 +000090 throw TException("Unexpected call to TEvhttpServer::serve");
David Reiss5ddabb82010-10-06 17:09:37 +000091 }
92 return event_base_dispatch(eb_);
93}
94
Konrad Grochowski16a23a62014-11-13 15:33:38 +010095TEvhttpServer::RequestContext::RequestContext(struct evhttp_request* req)
96 : req(req),
97 ibuf(new TMemoryBuffer(EVBUFFER_DATA(req->input_buffer),
98 static_cast<uint32_t>(EVBUFFER_LENGTH(req->input_buffer)))),
99 obuf(new TMemoryBuffer()) {
100}
David Reiss5ddabb82010-10-06 17:09:37 +0000101
102void TEvhttpServer::request(struct evhttp_request* req, void* self) {
Roger Meier7e056e72011-07-17 07:28:28 +0000103 try {
104 static_cast<TEvhttpServer*>(self)->process(req);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100105 } catch (std::exception& e) {
Sebastian Zenker042580f2019-01-29 15:48:12 +0100106 evhttp_send_reply(req, HTTP_INTERNAL, e.what(), nullptr);
Roger Meier7e056e72011-07-17 07:28:28 +0000107 }
David Reiss5ddabb82010-10-06 17:09:37 +0000108}
109
David Reiss5ddabb82010-10-06 17:09:37 +0000110void TEvhttpServer::process(struct evhttp_request* req) {
Sebastian Zenker042580f2019-01-29 15:48:12 +0100111 auto* ctx = new RequestContext(req);
cyy316723a2019-01-05 16:35:14 +0800112 return processor_->process(std::bind(&TEvhttpServer::complete,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100113 this,
114 ctx,
cyy316723a2019-01-05 16:35:14 +0800115 std::placeholders::_1),
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100116 ctx->ibuf,
117 ctx->obuf);
David Reiss5ddabb82010-10-06 17:09:37 +0000118}
119
David Reiss5ddabb82010-10-06 17:09:37 +0000120void TEvhttpServer::complete(RequestContext* ctx, bool success) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100121 (void)success;
cyy316723a2019-01-05 16:35:14 +0800122 std::unique_ptr<RequestContext> ptr(ctx);
David Reiss5ddabb82010-10-06 17:09:37 +0000123
Roger Meier08077bf2011-09-11 07:28:54 +0000124 int code = success ? 200 : 400;
125 const char* reason = success ? "OK" : "Bad Request";
David Reiss5ddabb82010-10-06 17:09:37 +0000126
127 int rv = evhttp_add_header(ctx->req->output_headers, "Content-Type", "application/x-thrift");
128 if (rv != 0) {
129 // TODO: Log an error.
Roger Meier08077bf2011-09-11 07:28:54 +0000130 std::cerr << "evhttp_add_header failed " << __FILE__ << ":" << __LINE__ << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000131 }
132
133 struct evbuffer* buf = evbuffer_new();
Sebastian Zenker042580f2019-01-29 15:48:12 +0100134 if (buf == nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +0000135 // TODO: Log an error.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100136 std::cerr << "evbuffer_new failed " << __FILE__ << ":" << __LINE__ << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000137 } else {
138 uint8_t* obuf;
139 uint32_t sz;
140 ctx->obuf->getBuffer(&obuf, &sz);
141 int ret = evbuffer_add(buf, obuf, sz);
142 if (ret != 0) {
143 // TODO: Log an error.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100144 std::cerr << "evhttp_add failed with " << ret << " " << __FILE__ << ":" << __LINE__
145 << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000146 }
147 }
148
149 evhttp_send_reply(ctx->req, code, reason, buf);
Sebastian Zenker042580f2019-01-29 15:48:12 +0100150 if (buf != nullptr) {
David Reiss5ddabb82010-10-06 17:09:37 +0000151 evbuffer_free(buf);
152 }
153}
154
David Reiss5ddabb82010-10-06 17:09:37 +0000155struct event_base* TEvhttpServer::getEventBase() {
156 return eb_;
157}
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100158}
159}
160} // apache::thrift::async