| Kea 3.0.2-git

http_command_response_creator.cc
Go to the documentation of this file.
1// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <config/http_command_response_creator.h>
10#include <config/command_mgr.h>
11#include <config/config_log.h>
12#include <hooks/callout_handle.h>
13#include <hooks/hooks_log.h>
14#include <hooks/hooks_manager.h>
15#include <http/post_request_json.h>
16#include <http/response_json.h>
17#include <boost/pointer_cast.hpp>
18#include <iostream>
19
20 using namespace isc::config;
21 using namespace isc::data;
22 using namespace isc::hooks;
23 using namespace isc::http;
24 using namespace std;
25
26 namespace {
27
29 struct HttpCommandHooks {
30 int hook_index_http_auth_;
31 int hook_index_http_response_;
32
34 HttpCommandHooks() {
35 hook_index_http_auth_ = HooksManager::registerHook("http_auth");
36 hook_index_http_response_ = HooksManager::registerHook("http_response");
37 }
38};
39
40} // end of anonymous namespace.
41
42// Declare a Hooks object. As this is outside any function or method, it
43// will be instantiated (and the constructor run) when the module is loaded.
44// As a result, the hook indexes will be defined before any method in this
45// module is called.
46HttpCommandHooks Hooks;
47
48 namespace isc {
49 namespace config {
50
51 HttpRequestPtr
52 HttpCommandResponseCreator::createNewHttpRequest() const {
53 return (HttpRequestPtr(new PostHttpRequestJson()));
54}
55
56 HttpResponsePtr
57 HttpCommandResponseCreator::
58 createStockHttpResponse(const HttpRequestPtr& request,
59 const HttpStatusCode& status_code) const {
60 HttpResponsePtr response = createStockHttpResponseInternal(request, status_code);
61 response->finalize();
62 return (response);
63}
64
65 HttpResponsePtr
66HttpCommandResponseCreator::
67createStockHttpResponseInternal(const HttpRequestPtr& request,
68 const HttpStatusCode& status_code) const {
69 // The request hasn't been finalized so the request object
70 // doesn't contain any information about the HTTP version number
71 // used. But, the context should have this data (assuming the
72 // HTTP version is parsed OK).
73 HttpVersion http_version(request->context()->http_version_major_,
74 request->context()->http_version_minor_);
75 // We only accept HTTP version 1.0 or 1.1. If other version number is found
76 // we fall back to HTTP/1.0.
77 if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
78 http_version.major_ = 1;
79 http_version.minor_ = 0;
80 }
81 // This will generate the response holding JSON content.
82 HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
83 // Add extra headers.
84 if (config_) {
85 copyHttpHeaders(config_->getHttpHeaders(), *response);
86 }
87 return (response);
88}
89
90 HttpResponsePtr
91HttpCommandResponseCreator::createDynamicHttpResponse(HttpRequestPtr request) {
92 CfgHttpHeaders headers;
93 HttpResponseJsonPtr http_response;
94
95 // Check the basic HTTP authentication.
96 if (config_) {
97 headers = config_->getHttpHeaders();
98 const HttpAuthConfigPtr& auth = config_->getAuthConfig();
99 if (auth) {
100 http_response = auth->checkAuth(*this, request);
101 }
102 }
103
104 // Pass extra headers to the hook.
105 bool auth_failed = false;
106 if (http_response) {
107 auth_failed = true;
108 copyHttpHeaders(headers, *http_response);
109 }
110
111 // Callout point for "http_auth".
112 bool reset_handle = false;
113 if (HooksManager::calloutsPresent(Hooks.hook_index_http_auth_)) {
114 // Get callout handle.
115 CalloutHandlePtr callout_handle = request->getCalloutHandle();
116 ScopedCalloutHandleState callout_handle_state(callout_handle);
117
118 // Pass arguments.
119 callout_handle->setArgument("request", request);
120 callout_handle->setArgument("response", http_response);
121
122 // Call callouts.
123 HooksManager::callCallouts(Hooks.hook_index_http_auth_,
124 *callout_handle);
125 callout_handle->getArgument("request", request);
126 callout_handle->getArgument("response", http_response);
127
128 // Status other than continue means 'please reset the handle'.
129 if (callout_handle->getStatus() != CalloutHandle::NEXT_STEP_CONTINUE) {
130 reset_handle = true;
131 }
132 }
133
134 // The basic HTTP authentication check or a callout failed and
135 // left a response.
136 if (http_response) {
137 // Avoid to copy extra headers twice even this should not be required.
138 if (!auth_failed && !headers.empty()) {
139 copyHttpHeaders(headers, *http_response);
140 if (http_response->isFinalized()) {
141 // Argh! The response was already finalized.
142 http_response->reset();
143 http_response->finalize();
144 }
145 }
146 return (http_response);
147 }
148
149 // Reset the handle when a hook asks for.
150 if (reset_handle) {
151 request->resetCalloutHandle();
152 }
153
154 // The request is always non-null, because this is verified by the
155 // createHttpResponse method. Let's try to convert it to the
156 // PostHttpRequestJson type as this is the type generated by the
157 // createNewHttpRequest. If the conversion result is null it means that
158 // the caller did not use createNewHttpRequest method to create this
159 // instance. This is considered an error in the server logic.
160 PostHttpRequestJsonPtr request_json =
161 boost::dynamic_pointer_cast<PostHttpRequestJson>(request);
162 if (!request_json) {
163 // Notify the client that we have a problem with our server.
164 return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
165 }
166
167 // We have already checked that the request is finalized so the call
168 // to getBodyAsJson must not trigger an exception.
169 ConstElementPtr command = request_json->getBodyAsJson();
170
171 // Process command doesn't generate exceptions but can possibly return
172 // null response, if the handler is not implemented properly. This is
173 // again an internal server issue.
174 ConstElementPtr response = config::CommandMgr::instance().processCommand(command);
175
176 if (!response) {
177 // Notify the client that we have a problem with our server.
178 return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
179 }
180
181 // Normal Responses coming from the Kea server must always be wrapped in
182 // a list as they may contain responses from multiple daemons.
183 // If we're emulating that for backward compatibility, then we need to wrap
184 // the answer in a list if it isn't in one already.
185 if ((!config_ || config_->getEmulateAgentResponse()) &&
186 (response->getType() != Element::list)) {
187 ElementPtr response_list = Element::createList();
188 response_list->add(boost::const_pointer_cast<Element>(response));
189 response = response_list;
190 }
191
192 // The response is OK, so let's create new HTTP response with the status OK.
193 http_response = boost::dynamic_pointer_cast<
194 HttpResponseJson>(createStockHttpResponseInternal(request, HttpStatusCode::OK));
195 http_response->setBodyAsJson(response);
196 http_response->finalize();
197
198 // Callout point for "http_response".
199 if (HooksManager::calloutsPresent(Hooks.hook_index_http_response_)) {
200 // Get callout handle.
201 CalloutHandlePtr callout_handle = request->getCalloutHandle();
202 ScopedCalloutHandleState callout_handle_state(callout_handle);
203
204 // Pass arguments.
205 callout_handle->setArgument("request", request);
206 callout_handle->setArgument("response", http_response);
207
208 // Call callouts.
209 HooksManager::callCallouts(Hooks.hook_index_http_response_,
210 *callout_handle);
211 callout_handle->getArgument("response", http_response);
212
213 // Ignore status as the HTTP response is used instead.
214 }
215
216 return (http_response);
217}
218
219} // end of namespace isc::config
220} // end of namespace isc
Hooks
CtrlAgentHooks Hooks
Definition ca_response_creator.cc:47
callout_handle.h
CalloutHandle::NEXT_STEP_CONTINUE
@ NEXT_STEP_CONTINUE
continue normally
Definition callout_handle.h:87
Element::list
@ list
Definition data.h:146
Element::createList
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
isc::config::BaseCommandMgr::processCommand
virtual isc::data::ConstElementPtr processCommand(const isc::data::ConstElementPtr &cmd)
Triggers command processing.
Definition base_command_mgr.cc:119
isc::config::CommandMgr::instance
static CommandMgr & instance()
CommandMgr is a singleton class.
Definition command_mgr.cc:18
isc::config::HttpCommandResponseCreator::createNewHttpRequest
virtual http::HttpRequestPtr createNewHttpRequest() const
Create a new request.
Definition http_command_response_creator.cc:52
isc::config::HttpCommandResponseCreator::createStockHttpResponse
virtual http::HttpResponsePtr createStockHttpResponse(const http::HttpRequestPtr &request, const http::HttpStatusCode &status_code) const
Creates stock HTTP response.
Definition http_command_response_creator.cc:58
isc::hooks::HooksManager::registerHook
static int registerHook(const std::string &name)
Register Hook.
Definition hooks_manager.cc:232
isc::hooks::HooksManager::calloutsPresent
static bool calloutsPresent(int index)
Are callouts present?
Definition hooks_manager.cc:54
isc::hooks::HooksManager::callCallouts
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
Definition hooks_manager.cc:76
isc::http::HttpResponseJson
Represents HTTP response with JSON content.
Definition response_json.h:34
isc::http::PostHttpRequestJson
Represents HTTP POST request with JSON body.
Definition post_request_json.h:37
command_mgr.h
config_log.h
hooks_log.h
hooks_manager.h
http_command_response_creator.h
isc::config
Definition command_interpreter.cc:23
isc::data
Definition base_stamped_element.cc:12
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
isc::hooks
Definition callout_handle.cc:21
isc::hooks::CalloutHandlePtr
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
Definition callout_handle.h:470
isc::http
Definition auth_config.h:19
isc::http::HttpStatusCode
HttpStatusCode
HTTP status codes (cf RFC 2068).
Definition response.h:30
isc::http::copyHttpHeaders
void copyHttpHeaders(const CfgHttpHeaders &headers, const HTTP_MSG &message)
Copy config HTTP headers to message.
Definition cfg_http_header.h:55
isc::http::PostHttpRequestJsonPtr
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
Definition post_request_json.h:29
isc::http::HttpAuthConfigPtr
boost::shared_ptr< HttpAuthConfig > HttpAuthConfigPtr
Type of shared pointers to HTTP authentication configuration.
Definition auth_config.h:97
isc::http::HttpResponseJsonPtr
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
Definition response_json.h:27
isc::http::HttpResponsePtr
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition response.h:81
isc::http::HttpRequestPtr
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition request.h:30
isc::http::CfgHttpHeaders
std::vector< CfgHttpHeader > CfgHttpHeaders
Collection of config HTTP headers.
Definition cfg_http_header.h:47
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148
post_request_json.h
response_json.h
isc::http::HttpVersion
HTTP protocol version.
Definition http_types.h:14