Skip to content

Commit cd8b3c9

Browse files
committed
Added closure_data to deferred_response
1 parent 1b48091 commit cd8b3c9

File tree

6 files changed

+174
-59
lines changed

6 files changed

+174
-59
lines changed

README.md

Lines changed: 74 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -601,10 +601,10 @@ There are 5 types of response that you can create - we will describe them here t
601601
* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file.
602602
* _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file.
603603
* _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file.
604-
* _deferred_response(**ssize_t(*cycle_callback_ptr)(char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default).
604+
* _deferred_response(**ssize_t(&ast;cycle_callback_ptr)(shared_ptr<T>, char&ast;, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default).
605605
* The `cycle_callback_ptr` has this shape:
606-
_**ssize_t** cycle_callback(**char&ast;** buf, **size_t** max_size)_.
607-
You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection.
606+
_**ssize_t** cycle_callback(**shared_ptr<T> closure_data, char&ast;** buf, **size_t** max_size)_.
607+
You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation.
608608

609609
### Setting additional properties of the response
610610
The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is:
@@ -825,36 +825,37 @@ You can also check this example on [github](https://github.com/etr/libhttpserver
825825

826826
#### Example of a deferred response through callback
827827
#include <httpserver.hpp>
828-
828+
829829
using namespace httpserver;
830-
830+
831831
static int counter = 0;
832-
833-
ssize_t test_callback (char* buf, size_t max) {
832+
833+
ssize_t test_callback (std::shared_ptr<void> closure_data, char* buf, size_t max) {
834834
if (counter == 2) {
835835
return -1;
836-
} else {
836+
}
837+
else {
837838
memset(buf, 0, max);
838839
strcat(buf, " test ");
839840
counter++;
840841
return std::string(buf).size();
841842
}
842843
}
843-
844+
844845
class deferred_resource : public http_resource {
845-
public:
846-
const std::shared_ptr<http_response> render_GET(const http_request& req) {
847-
return std::shared_ptr<deferred_response>(new deferred_response(test_callback, "cycle callback response"));
848-
}
846+
public:
847+
const std::shared_ptr<http_response> render_GET(const http_request& req) {
848+
return std::shared_ptr<deferred_response<void> >(new deferred_response<void>(test_callback, nullptr, "cycle callback response"));
849+
}
849850
};
850-
851+
851852
int main(int argc, char** argv) {
852853
webserver ws = create_webserver(8080);
853-
854+
854855
deferred_resource hwr;
855856
ws.register_resource("/hello", &hwr);
856857
ws.start(true);
857-
858+
858859
return 0;
859860
}
860861

@@ -864,6 +865,63 @@ To test the above example, you can run the following command from a terminal:
864865

865866
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp).
866867

868+
#### Example of a deferred response through callback (passing additional data along)
869+
#include <atomic>
870+
#include <httpserver.hpp>
871+
872+
using namespace httpserver;
873+
874+
std::atomic<int> counter;
875+
876+
ssize_t test_callback (std::shared_ptr<std::atomic<int> > closure_data, char* buf, size_t max) {
877+
int reqid;
878+
if (closure_data == nullptr) {
879+
reqid = -1;
880+
} else {
881+
reqid = *closure_data;
882+
}
883+
884+
// only first 5 connections can be established
885+
if (reqid >= 5) {
886+
return -1;
887+
} else {
888+
// respond corresponding request IDs to the clients
889+
std::string str = "";
890+
str += std::to_string(reqid) + " ";
891+
memset(buf, 0, max);
892+
std::copy(str.begin(), str.end(), buf);
893+
894+
// keep sending reqid
895+
sleep(1);
896+
897+
return (ssize_t)max;
898+
}
899+
}
900+
901+
class deferred_resource : public http_resource {
902+
public:
903+
const std::shared_ptr<http_response> render_GET(const http_request& req) {
904+
std::shared_ptr<std::atomic<int> > closure_data(new std::atomic<int>(counter++));
905+
return std::shared_ptr<deferred_response<std::atomic<int> > >(new deferred_response<std::atomic<int> >(test_callback, closure_data, "cycle callback response"));
906+
}
907+
};
908+
909+
int main(int argc, char** argv) {
910+
webserver ws = create_webserver(8080);
911+
912+
deferred_resource hwr;
913+
ws.register_resource("/hello", &hwr);
914+
ws.start(true);
915+
916+
return 0;
917+
}
918+
919+
To test the above example, you can run the following command from a terminal:
920+
921+
curl -XGET -v localhost:8080/hello
922+
923+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/deferred_with_accumulator.cpp).
924+
867925
[Back to TOC](#table-of-contents)
868926

869927
## Copying

examples/Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
LDADD = $(top_builddir)/src/libhttpserver.la
2020
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
2121
METASOURCES = AUTO
22-
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads
22+
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads deferred_with_accumulator
2323

2424
hello_world_SOURCES = hello_world.cpp
2525
service_SOURCES = service.cpp
@@ -35,6 +35,7 @@ digest_authentication_SOURCES = digest_authentication.cpp
3535
minimal_https_SOURCES = minimal_https.cpp
3636
minimal_file_response_SOURCES = minimal_file_response.cpp
3737
minimal_deferred_SOURCES = minimal_deferred.cpp
38+
deferred_with_accumulator_SOURCES = deferred_with_accumulator.cpp
3839
url_registration_SOURCES = url_registration.cpp
3940
minimal_ip_ban_SOURCES = minimal_ip_ban.cpp
4041
benchmark_select_SOURCES = benchmark_select.cpp

examples/minimal_deferred.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ using namespace httpserver;
2424

2525
static int counter = 0;
2626

27-
ssize_t test_callback (char* buf, size_t max) {
27+
ssize_t test_callback (std::shared_ptr<void> closure_data, char* buf, size_t max) {
2828
if (counter == 2) {
2929
return -1;
3030
}
@@ -39,7 +39,7 @@ ssize_t test_callback (char* buf, size_t max) {
3939
class deferred_resource : public http_resource {
4040
public:
4141
const std::shared_ptr<http_response> render_GET(const http_request& req) {
42-
return std::shared_ptr<deferred_response>(new deferred_response(test_callback, "cycle callback response"));
42+
return std::shared_ptr<deferred_response<void> >(new deferred_response<void>(test_callback, nullptr, "cycle callback response"));
4343
}
4444
};
4545

src/deferred_response.cpp

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,44 +28,18 @@ namespace httpserver
2828
namespace details
2929
{
3030

31-
ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max)
32-
{
33-
ssize_t val = static_cast<deferred_response*>(cls)->cycle_callback(buf, max);
34-
if(val == -1)
35-
{
36-
static_cast<deferred_response*>(cls)->completed = true;
37-
}
38-
39-
return val;
40-
}
41-
42-
}
43-
44-
MHD_Response* deferred_response::get_raw_response()
31+
MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t))
4532
{
4633
if(!completed)
4734
{
48-
return MHD_create_response_from_callback(
49-
MHD_SIZE_UNKNOWN,
50-
1024,
51-
&details::cb,
52-
this,
53-
NULL
54-
);
35+
return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL);
5536
}
5637
else
5738
{
58-
return static_cast<string_response*>(this)->get_raw_response();
39+
return static_cast<string_response*>(cls)->get_raw_response();
5940
}
6041
}
6142

62-
void deferred_response::decorate_response(MHD_Response* response)
63-
{
64-
if(completed)
65-
{
66-
static_cast<string_response*>(this)->decorate_response(response);
67-
}
6843
}
6944

7045
}
71-

src/httpserver/deferred_response.hpp

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,43 +25,47 @@
2525
#ifndef _DEFERRED_RESPONSE_HPP_
2626
#define _DEFERRED_RESPONSE_HPP_
2727

28+
#include <memory>
2829
#include "httpserver/string_response.hpp"
2930

3031
namespace httpserver
3132
{
3233

3334
namespace details
3435
{
35-
ssize_t cb(void*, uint64_t, char*, size_t);
36-
};
37-
38-
typedef ssize_t(*cycle_callback_ptr)(char*, size_t);
36+
MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t));
37+
}
3938

39+
template <class T>
4040
class deferred_response : public string_response
4141
{
4242
public:
4343
explicit deferred_response(
44-
cycle_callback_ptr cycle_callback,
44+
ssize_t(*cycle_callback)(std::shared_ptr<T>, char*, size_t),
45+
std::shared_ptr<T> closure_data,
4546
const std::string& content = "",
4647
int response_code = http::http_utils::http_ok,
4748
const std::string& content_type = http::http_utils::text_plain
4849
):
4950
string_response(content, response_code, content_type),
5051
cycle_callback(cycle_callback),
52+
closure_data(closure_data),
5153
completed(false)
5254
{
5355
}
5456

5557
deferred_response(const deferred_response& other):
5658
string_response(other),
5759
cycle_callback(other.cycle_callback),
60+
closure_data(other.closure_data),
5861
completed(other.completed)
5962
{
6063
}
6164

6265
deferred_response(deferred_response&& other) noexcept:
6366
string_response(std::move(other)),
6467
cycle_callback(std::move(other.cycle_callback)),
68+
closure_data(std::move(other.closure_data)),
6569
completed(other.completed)
6670
{
6771
}
@@ -72,6 +76,7 @@ class deferred_response : public string_response
7276

7377
(string_response&) (*this) = b;
7478
this->cycle_callback = b.cycle_callback;
79+
this->closure_data = b.closure_data;
7580
this->completed = b.completed;
7681

7782
return *this;
@@ -83,6 +88,7 @@ class deferred_response : public string_response
8388

8489
(string_response&) (*this) = std::move(b);
8590
this->cycle_callback = std::move(b.cycle_callback);
91+
this->closure_data = std::move(b.closure_data);
8692
this->completed = b.completed;
8793

8894
return *this;
@@ -92,13 +98,35 @@ class deferred_response : public string_response
9298
{
9399
}
94100

95-
MHD_Response* get_raw_response();
96-
void decorate_response(MHD_Response* response);
101+
MHD_Response* get_raw_response()
102+
{
103+
return details::get_raw_response_helper((void*) this, completed, &(this->cb));
104+
}
105+
106+
void decorate_response(MHD_Response* response)
107+
{
108+
if(completed)
109+
{
110+
static_cast<string_response*>(this)->decorate_response(response);
111+
}
112+
}
113+
97114
private:
98-
cycle_callback_ptr cycle_callback;
115+
ssize_t (*cycle_callback)(std::shared_ptr<T>, char*, size_t);
116+
std::shared_ptr<T> closure_data;
99117
bool completed;
100118

101-
friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max);
119+
static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max)
120+
{
121+
deferred_response<T>* dfr = static_cast<deferred_response<T>*>(cls);
122+
ssize_t val = dfr->cycle_callback(dfr->closure_data, buf, max);
123+
if(val == -1)
124+
{
125+
dfr->completed = true;
126+
}
127+
128+
return val;
129+
}
102130
};
103131

104132
}

0 commit comments

Comments
 (0)