MXS-1220: Add support for HEAD and OPTIONS methods

All resources now support HEAD and OPTIONS methods.

The HEAD response is generated by truncating a GET request on the same
resource. This does not work with other methods as they could require a
message body.
This commit is contained in:
Markus Mäkelä
2017-04-20 19:30:57 +03:00
committed by Markus Mäkelä
parent 47d819b008
commit 99f25b1c31

View File

@ -13,6 +13,7 @@
#include "maxscale/resource.hh" #include "maxscale/resource.hh"
#include <list> #include <list>
#include <sstream>
#include <maxscale/alloc.h> #include <maxscale/alloc.h>
#include <maxscale/jansson.hh> #include <maxscale/jansson.hh>
@ -28,6 +29,7 @@
using std::list; using std::list;
using std::string; using std::string;
using std::stringstream;
using mxs::SpinLock; using mxs::SpinLock;
using mxs::SpinLockGuard; using mxs::SpinLockGuard;
@ -288,6 +290,11 @@ HttpResponse cb_modules(const HttpRequest& request)
return HttpResponse(MHD_HTTP_OK); return HttpResponse(MHD_HTTP_OK);
} }
HttpResponse cb_send_ok(const HttpRequest& request)
{
return HttpResponse(MHD_HTTP_OK);
}
class RootResource class RootResource
{ {
RootResource(const RootResource&); RootResource(const RootResource&);
@ -298,6 +305,10 @@ public:
RootResource() RootResource()
{ {
// Special resources required by OPTION etc.
m_get.push_back(SResource(new Resource(cb_send_ok, 1, "/")));
m_get.push_back(SResource(new Resource(cb_send_ok, 1, "*")));
m_get.push_back(SResource(new Resource(cb_all_servers, 1, "servers"))); m_get.push_back(SResource(new Resource(cb_all_servers, 1, "servers")));
m_get.push_back(SResource(new Resource(cb_get_server, 2, "servers", ":server"))); m_get.push_back(SResource(new Resource(cb_get_server, 2, "servers", ":server")));
@ -330,44 +341,126 @@ public:
{ {
} }
HttpResponse process_request_type(ResourceList& list, HttpRequest& request) ResourceList::const_iterator find_resource(const ResourceList& list, const HttpRequest& request) const
{ {
for (ResourceList::iterator it = list.begin(); it != list.end(); it++) for (ResourceList::const_iterator it = list.begin(); it != list.end(); it++)
{ {
Resource& r = *(*it); Resource& r = *(*it);
if (r.match(request)) if (r.match(request))
{ {
return r.call(request); return it;
} }
} }
return list.end();
}
HttpResponse process_request_type(const ResourceList& list, const HttpRequest& request)
{
ResourceList::const_iterator it = find_resource(list, request);
if (it != list.end())
{
Resource& r = *(*it);
return r.call(request);
}
return HttpResponse(MHD_HTTP_NOT_FOUND); return HttpResponse(MHD_HTTP_NOT_FOUND);
} }
HttpResponse process_request(HttpRequest& request) string get_supported_methods(const HttpRequest& request)
{ {
if (request.get_verb() == "GET") list<string> l;
if (find_resource(m_get, request) != m_get.end())
{
l.push_back(MHD_HTTP_METHOD_GET);
}
if (find_resource(m_put, request) != m_put.end())
{
l.push_back(MHD_HTTP_METHOD_PUT);
}
if (find_resource(m_post, request) != m_post.end())
{
l.push_back(MHD_HTTP_METHOD_POST);
}
if (find_resource(m_delete, request) != m_delete.end())
{
l.push_back(MHD_HTTP_METHOD_DELETE);
}
if (find_resource(m_patch, request) != m_patch.end())
{
l.push_back(MHD_HTTP_METHOD_PATCH);
}
stringstream rval;
if (l.size() > 0)
{
rval << l.front();
l.pop_front();
}
for (list<string>::iterator it = l.begin(); it != l.end(); it++)
{
rval << ", " << *it;
}
return rval.str();
}
HttpResponse process_request(const HttpRequest& request)
{
if (request.get_verb() == MHD_HTTP_METHOD_GET)
{ {
return process_request_type(m_get, request); return process_request_type(m_get, request);
} }
else if (request.get_verb() == "PUT") else if (request.get_verb() == MHD_HTTP_METHOD_PUT)
{ {
return process_request_type(m_put, request); return process_request_type(m_put, request);
} }
else if (request.get_verb() == "POST") else if (request.get_verb() == MHD_HTTP_METHOD_POST)
{ {
return process_request_type(m_post, request); return process_request_type(m_post, request);
} }
else if (request.get_verb() == MHD_HTTP_METHOD_PATCH)
{
return process_request_type(m_patch, request);
}
else if (request.get_verb() == MHD_HTTP_METHOD_DELETE)
{
return process_request_type(m_delete, request);
}
else if (request.get_verb() == MHD_HTTP_METHOD_OPTIONS)
{
string methods = get_supported_methods(request);
if (methods.size() > 0)
{
HttpResponse response(MHD_HTTP_OK);
response.add_header(HTTP_RESPONSE_HEADER_ACCEPT, methods);
return response;
}
}
else if (request.get_verb() == MHD_HTTP_METHOD_HEAD)
{
/** Do a GET and just drop the body of the response */
HttpResponse response = process_request_type(m_get, request);
response.drop_response();
return response;
}
return HttpResponse(MHD_HTTP_METHOD_NOT_ALLOWED); return HttpResponse(MHD_HTTP_METHOD_NOT_ALLOWED);
} }
private: private:
ResourceList m_get; /**< GET request handlers */ ResourceList m_get; /**< GET request handlers */
ResourceList m_put; /**< PUT request handlers */ ResourceList m_put; /**< PUT request handlers */
ResourceList m_post; /**< POST request handlers */ ResourceList m_post; /**< POST request handlers */
ResourceList m_delete; /**< DELETE request handlers */
ResourceList m_patch; /**< PATCH request handlers */
}; };
static RootResource resources; /**< Core resource set */ static RootResource resources; /**< Core resource set */