David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 1 | /* |
| 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 Meier | 4285ba2 | 2013-06-10 21:17:23 +0200 | [diff] [blame] | 20 | #include <thrift/async/TEvhttpServer.h> |
| 21 | #include <thrift/async/TAsyncBufferProcessor.h> |
Roger Meier | 49ff8b1 | 2012-04-13 09:12:31 +0000 | [diff] [blame] | 22 | #include <thrift/transport/TBufferTransports.h> |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 23 | #include <memory> |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 24 | #include <evhttp.h> |
Ben Craig | 7207c22 | 2015-07-06 08:40:35 -0500 | [diff] [blame] | 25 | #include <event2/buffer.h> |
| 26 | #include <event2/buffer_compat.h> |
Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 27 | #include <iostream> |
| 28 | |
Roger Meier | 7e056e7 | 2011-07-17 07:28:28 +0000 | [diff] [blame] | 29 | #ifndef HTTP_INTERNAL // libevent < 2 |
| 30 | #define HTTP_INTERNAL 500 |
| 31 | #endif |
| 32 | |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 33 | using apache::thrift::transport::TMemoryBuffer; |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 34 | using std::shared_ptr; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 35 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 36 | namespace apache { |
| 37 | namespace thrift { |
| 38 | namespace async { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 39 | |
| 40 | struct TEvhttpServer::RequestContext { |
| 41 | struct evhttp_request* req; |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 42 | std::shared_ptr<apache::thrift::transport::TMemoryBuffer> ibuf; |
| 43 | std::shared_ptr<apache::thrift::transport::TMemoryBuffer> obuf; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 44 | |
| 45 | RequestContext(struct evhttp_request* req); |
| 46 | }; |
| 47 | |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 48 | TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor) |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 49 | : processor_(processor), eb_(nullptr), eh_(nullptr) { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 50 | } |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 51 | |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 52 | TEvhttpServer::TEvhttpServer(std::shared_ptr<TAsyncBufferProcessor> processor, int port) |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 53 | : processor_(processor), eb_(nullptr), eh_(nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 54 | // Create event_base and evhttp. |
| 55 | eb_ = event_base_new(); |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 56 | if (eb_ == nullptr) { |
Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 57 | throw TException("event_base_new failed"); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 58 | } |
| 59 | eh_ = evhttp_new(eb_); |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 60 | if (eh_ == nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 61 | event_base_free(eb_); |
Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 62 | throw TException("evhttp_new failed"); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 63 | } |
| 64 | |
| 65 | // Bind to port. |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 66 | int ret = evhttp_bind_socket(eh_, nullptr, port); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 67 | if (ret < 0) { |
| 68 | evhttp_free(eh_); |
| 69 | event_base_free(eb_); |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 70 | throw TException("evhttp_bind_socket failed"); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 71 | } |
| 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 Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 79 | TEvhttpServer::~TEvhttpServer() { |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 80 | if (eh_ != nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 81 | evhttp_free(eh_); |
| 82 | } |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 83 | if (eb_ != nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 84 | event_base_free(eb_); |
| 85 | } |
| 86 | } |
| 87 | |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 88 | int TEvhttpServer::serve() { |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 89 | if (eb_ == nullptr) { |
Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 90 | throw TException("Unexpected call to TEvhttpServer::serve"); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 91 | } |
| 92 | return event_base_dispatch(eb_); |
| 93 | } |
| 94 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 95 | TEvhttpServer::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 Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 101 | |
| 102 | void TEvhttpServer::request(struct evhttp_request* req, void* self) { |
Roger Meier | 7e056e7 | 2011-07-17 07:28:28 +0000 | [diff] [blame] | 103 | try { |
| 104 | static_cast<TEvhttpServer*>(self)->process(req); |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 105 | } catch (std::exception& e) { |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 106 | evhttp_send_reply(req, HTTP_INTERNAL, e.what(), nullptr); |
Roger Meier | 7e056e7 | 2011-07-17 07:28:28 +0000 | [diff] [blame] | 107 | } |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 108 | } |
| 109 | |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 110 | void TEvhttpServer::process(struct evhttp_request* req) { |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 111 | auto* ctx = new RequestContext(req); |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 112 | return processor_->process(std::bind(&TEvhttpServer::complete, |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 113 | this, |
| 114 | ctx, |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 115 | std::placeholders::_1), |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 116 | ctx->ibuf, |
| 117 | ctx->obuf); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 118 | } |
| 119 | |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 120 | void TEvhttpServer::complete(RequestContext* ctx, bool success) { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 121 | (void)success; |
cyy | 316723a | 2019-01-05 16:35:14 +0800 | [diff] [blame] | 122 | std::unique_ptr<RequestContext> ptr(ctx); |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 123 | |
Roger Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 124 | int code = success ? 200 : 400; |
| 125 | const char* reason = success ? "OK" : "Bad Request"; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 126 | |
| 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 Meier | 08077bf | 2011-09-11 07:28:54 +0000 | [diff] [blame] | 130 | std::cerr << "evhttp_add_header failed " << __FILE__ << ":" << __LINE__ << std::endl; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | struct evbuffer* buf = evbuffer_new(); |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 134 | if (buf == nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 135 | // TODO: Log an error. |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 136 | std::cerr << "evbuffer_new failed " << __FILE__ << ":" << __LINE__ << std::endl; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 137 | } 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 Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 144 | std::cerr << "evhttp_add failed with " << ret << " " << __FILE__ << ":" << __LINE__ |
| 145 | << std::endl; |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | |
| 149 | evhttp_send_reply(ctx->req, code, reason, buf); |
Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 150 | if (buf != nullptr) { |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 151 | evbuffer_free(buf); |
| 152 | } |
| 153 | } |
| 154 | |
David Reiss | 5ddabb8 | 2010-10-06 17:09:37 +0000 | [diff] [blame] | 155 | struct event_base* TEvhttpServer::getEventBase() { |
| 156 | return eb_; |
| 157 | } |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 158 | } |
| 159 | } |
| 160 | } // apache::thrift::async |