Skip to content

Commit a094976

Browse files
committed
Add GetQueryParameter helper function
Easily get the query parameter from the URI, with optional default value.
1 parent fff771e commit a094976

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ BITCOIN_TESTS =\
9595
test/fs_tests.cpp \
9696
test/getarg_tests.cpp \
9797
test/hash_tests.cpp \
98+
test/httpserver_tests.cpp \
9899
test/i2p_tests.cpp \
99100
test/interfaces_tests.cpp \
100101
test/key_io_tests.cpp \

src/httpserver.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@
2323

2424
#include <deque>
2525
#include <memory>
26+
#include <optional>
2627
#include <stdio.h>
2728
#include <stdlib.h>
2829
#include <string>
2930

3031
#include <sys/types.h>
3132
#include <sys/stat.h>
3233

33-
#include <event2/thread.h>
3434
#include <event2/buffer.h>
3535
#include <event2/bufferevent.h>
36-
#include <event2/util.h>
36+
#include <event2/http.h>
3737
#include <event2/keyvalq_struct.h>
38+
#include <event2/thread.h>
39+
#include <event2/util.h>
3840

3941
#include <support/events.h>
4042

@@ -639,6 +641,37 @@ HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const
639641
}
640642
}
641643

644+
std::optional<std::string> HTTPRequest::GetQueryParameter(const std::string& key) const
645+
{
646+
const char* uri{evhttp_request_get_uri(req)};
647+
648+
return GetQueryParameterFromUri(uri, key);
649+
}
650+
651+
std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key)
652+
{
653+
evhttp_uri* uri_parsed{evhttp_uri_parse(uri)};
654+
const char* query{evhttp_uri_get_query(uri_parsed)};
655+
std::optional<std::string> result;
656+
657+
if (query) {
658+
// Parse the query string into a key-value queue and iterate over it
659+
struct evkeyvalq params_q;
660+
evhttp_parse_query_str(query, &params_q);
661+
662+
for (struct evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) {
663+
if (param->key == key) {
664+
result = param->value;
665+
break;
666+
}
667+
}
668+
evhttp_clear_headers(&params_q);
669+
}
670+
evhttp_uri_free(uri_parsed);
671+
672+
return result;
673+
}
674+
642675
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
643676
{
644677
LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);

src/httpserver.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
#ifndef BITCOIN_HTTPSERVER_H
66
#define BITCOIN_HTTPSERVER_H
77

8-
#include <string>
98
#include <functional>
9+
#include <optional>
10+
#include <string>
1011

1112
static const int DEFAULT_HTTP_THREADS=4;
1213
static const int DEFAULT_HTTP_WORKQUEUE=16;
@@ -83,6 +84,17 @@ class HTTPRequest
8384
*/
8485
RequestMethod GetRequestMethod() const;
8586

87+
/** Get the query parameter value from request uri for a specified key, or std::nullopt if the
88+
* key is not found.
89+
*
90+
* If the query string contains duplicate keys, the first value is returned. Many web frameworks
91+
* would instead parse this as an array of values, but this is not (yet) implemented as it is
92+
* currently not needed in any of the endpoints.
93+
*
94+
* @param[in] key represents the query parameter of which the value is returned
95+
*/
96+
std::optional<std::string> GetQueryParameter(const std::string& key) const;
97+
8698
/**
8799
* Get the request header specified by hdr, or an empty string.
88100
* Return a pair (isPresent,string).
@@ -115,6 +127,20 @@ class HTTPRequest
115127
void WriteReply(int nStatus, const std::string& strReply = "");
116128
};
117129

130+
/** Get the query parameter value from request uri for a specified key, or std::nullopt if the key
131+
* is not found.
132+
*
133+
* If the query string contains duplicate keys, the first value is returned. Many web frameworks
134+
* would instead parse this as an array of values, but this is not (yet) implemented as it is
135+
* currently not needed in any of the endpoints.
136+
*
137+
* Helper function for HTTPRequest::GetQueryParameter.
138+
*
139+
* @param[in] uri is the entire request uri
140+
* @param[in] key represents the query parameter of which the value is returned
141+
*/
142+
std::optional<std::string> GetQueryParameterFromUri(const char* uri, const std::string& key);
143+
118144
/** Event handler closure.
119145
*/
120146
class HTTPClosure

src/test/httpserver_tests.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2012-2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <httpserver.h>
6+
#include <test/util/setup_common.h>
7+
8+
#include <boost/test/unit_test.hpp>
9+
10+
BOOST_FIXTURE_TEST_SUITE(httpserver_tests, BasicTestingSetup)
11+
12+
BOOST_AUTO_TEST_CASE(test_query_parameters)
13+
{
14+
std::string uri {};
15+
16+
// No parameters
17+
uri = "localhost:8080/rest/headers/someresource.json";
18+
BOOST_CHECK(!GetQueryParameterFromUri(uri.c_str(), "p1").has_value());
19+
20+
// Single parameter
21+
uri = "localhost:8080/rest/endpoint/someresource.json?p1=v1";
22+
BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri.c_str(), "p1").value(), "v1");
23+
BOOST_CHECK(!GetQueryParameterFromUri(uri.c_str(), "p2").has_value());
24+
25+
// Multiple parameters
26+
uri = "/rest/endpoint/someresource.json?p1=v1&p2=v2";
27+
BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri.c_str(), "p1").value(), "v1");
28+
BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri.c_str(), "p2").value(), "v2");
29+
30+
// If the query string contains duplicate keys, the first value is returned
31+
uri = "/rest/endpoint/someresource.json?p1=v1&p1=v2";
32+
BOOST_CHECK_EQUAL(GetQueryParameterFromUri(uri.c_str(), "p1").value(), "v1");
33+
34+
// Invalid query string syntax is the same as not having parameters
35+
uri = "/rest/endpoint/someresource.json&p1=v1&p2=v2";
36+
BOOST_CHECK(!GetQueryParameterFromUri(uri.c_str(), "p1").has_value());
37+
}
38+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)