blob: b92422c690006f282d4c6f9b9f118fcc89b2cec3 [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
20#include "TEvhttpServer.h"
21#include "TAsyncBufferProcessor.h"
22#include "transport/TBufferTransports.h"
23#include <evhttp.h>
24
Roger Meier08077bf2011-09-11 07:28:54 +000025#include <iostream>
26
Roger Meier7e056e72011-07-17 07:28:28 +000027#ifndef HTTP_INTERNAL // libevent < 2
28#define HTTP_INTERNAL 500
29#endif
30
David Reiss5ddabb82010-10-06 17:09:37 +000031using apache::thrift::transport::TMemoryBuffer;
32
33namespace apache { namespace thrift { namespace async {
34
35
36struct TEvhttpServer::RequestContext {
37 struct evhttp_request* req;
38 boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> ibuf;
39 boost::shared_ptr<apache::thrift::transport::TMemoryBuffer> obuf;
40
41 RequestContext(struct evhttp_request* req);
42};
43
44
45TEvhttpServer::TEvhttpServer(boost::shared_ptr<TAsyncBufferProcessor> processor)
46 : processor_(processor)
47 , eb_(NULL)
48 , eh_(NULL)
49{}
50
51
52TEvhttpServer::TEvhttpServer(boost::shared_ptr<TAsyncBufferProcessor> processor, int port)
53 : processor_(processor)
54 , eb_(NULL)
55 , eh_(NULL)
56{
57 // Create event_base and evhttp.
58 eb_ = event_base_new();
59 if (eb_ == NULL) {
Roger Meier08077bf2011-09-11 07:28:54 +000060 throw TException("event_base_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000061 }
62 eh_ = evhttp_new(eb_);
63 if (eh_ == NULL) {
64 event_base_free(eb_);
Roger Meier08077bf2011-09-11 07:28:54 +000065 throw TException("evhttp_new failed");
David Reiss5ddabb82010-10-06 17:09:37 +000066 }
67
68 // Bind to port.
69 int ret = evhttp_bind_socket(eh_, NULL, port);
70 if (ret < 0) {
71 evhttp_free(eh_);
72 event_base_free(eb_);
Roger Meier08077bf2011-09-11 07:28:54 +000073 throw TException("evhttp_bind_socket failed");
David Reiss5ddabb82010-10-06 17:09:37 +000074 }
75
76 // Register a handler. If you use the other constructor,
77 // you will want to do this yourself.
78 // Don't forget to unregister before destorying this TEvhttpServer.
79 evhttp_set_cb(eh_, "/", request, (void*)this);
80}
81
82
83TEvhttpServer::~TEvhttpServer() {
84 if (eh_ != NULL) {
85 evhttp_free(eh_);
86 }
87 if (eb_ != NULL) {
88 event_base_free(eb_);
89 }
90}
91
92
93int TEvhttpServer::serve() {
94 if (eb_ == NULL) {
Roger Meier08077bf2011-09-11 07:28:54 +000095 throw TException("Unexpected call to TEvhttpServer::serve");
David Reiss5ddabb82010-10-06 17:09:37 +000096 }
97 return event_base_dispatch(eb_);
98}
99
100
101TEvhttpServer::RequestContext::RequestContext(struct evhttp_request* req) : req(req)
102 , ibuf(new TMemoryBuffer(EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer)))
103 , obuf(new TMemoryBuffer())
104{}
105
106
107void TEvhttpServer::request(struct evhttp_request* req, void* self) {
Roger Meier7e056e72011-07-17 07:28:28 +0000108 try {
109 static_cast<TEvhttpServer*>(self)->process(req);
110 } catch(std::exception& e) {
111 evhttp_send_reply(req, HTTP_INTERNAL, e.what(), 0);
112 }
David Reiss5ddabb82010-10-06 17:09:37 +0000113}
114
115
116void TEvhttpServer::process(struct evhttp_request* req) {
117 RequestContext* ctx = new RequestContext(req);
118 return processor_->process(
119 std::tr1::bind(
120 &TEvhttpServer::complete,
121 this,
122 ctx,
123 std::tr1::placeholders::_1),
124 ctx->ibuf,
125 ctx->obuf);
126}
127
128
129void TEvhttpServer::complete(RequestContext* ctx, bool success) {
Roger Meiera8cef6e2011-07-17 18:55:59 +0000130 (void) success;
David Reiss5ddabb82010-10-06 17:09:37 +0000131 std::auto_ptr<RequestContext> ptr(ctx);
132
Roger Meier08077bf2011-09-11 07:28:54 +0000133 int code = success ? 200 : 400;
134 const char* reason = success ? "OK" : "Bad Request";
David Reiss5ddabb82010-10-06 17:09:37 +0000135
136 int rv = evhttp_add_header(ctx->req->output_headers, "Content-Type", "application/x-thrift");
137 if (rv != 0) {
138 // TODO: Log an error.
Roger Meier08077bf2011-09-11 07:28:54 +0000139 std::cerr << "evhttp_add_header failed " << __FILE__ << ":" << __LINE__ << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000140 }
141
142 struct evbuffer* buf = evbuffer_new();
143 if (buf == NULL) {
144 // TODO: Log an error.
Roger Meier08077bf2011-09-11 07:28:54 +0000145 std::cerr << "evbuffer_new failed " << __FILE__ << ":" << __LINE__ << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000146 } else {
147 uint8_t* obuf;
148 uint32_t sz;
149 ctx->obuf->getBuffer(&obuf, &sz);
150 int ret = evbuffer_add(buf, obuf, sz);
151 if (ret != 0) {
152 // TODO: Log an error.
Roger Meier08077bf2011-09-11 07:28:54 +0000153 std::cerr << "evhttp_add failed with " << ret << " " << __FILE__ << ":" << __LINE__ << std::endl;
David Reiss5ddabb82010-10-06 17:09:37 +0000154 }
155 }
156
157 evhttp_send_reply(ctx->req, code, reason, buf);
158 if (buf != NULL) {
159 evbuffer_free(buf);
160 }
161}
162
163
164struct event_base* TEvhttpServer::getEventBase() {
165 return eb_;
166}
167
168
169}}} // apache::thrift::async