Skip to content

Commit 78fa817

Browse files
authored
Merge pull request #145 from etr/deferred_response_data_2
Add data closure to the cycle_callback of deferred response
2 parents ead9ccc + ed0841d commit 78fa817

File tree

7 files changed

+229
-71
lines changed

7 files changed

+229
-71
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(*cycle_callback_ptr)(shared_ptr<T>, 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).
605605
* The `cycle_callback_ptr` has this shape:
606-
_**ssize_t** cycle_callback(**char*** 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*** 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
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <atomic>
22+
#include <httpserver.hpp>
23+
24+
using namespace httpserver;
25+
26+
std::atomic<int> counter;
27+
28+
ssize_t test_callback (std::shared_ptr<std::atomic<int> > closure_data, char* buf, size_t max) {
29+
int reqid;
30+
if (closure_data == nullptr) {
31+
reqid = -1;
32+
} else {
33+
reqid = *closure_data;
34+
}
35+
36+
// only first 5 connections can be established
37+
if (reqid >= 5) {
38+
return -1;
39+
} else {
40+
// respond corresponding request IDs to the clients
41+
std::string str = "";
42+
str += std::to_string(reqid) + " ";
43+
memset(buf, 0, max);
44+
std::copy(str.begin(), str.end(), buf);
45+
46+
// keep sending reqid
47+
sleep(1);
48+
49+
return (ssize_t)max;
50+
}
51+
}
52+
53+
class deferred_resource : public http_resource {
54+
public:
55+
const std::shared_ptr<http_response> render_GET(const http_request& req) {
56+
std::shared_ptr<std::atomic<int> > closure_data(new std::atomic<int>(counter++));
57+
return std::shared_ptr<deferred_response<std::atomic<int> > >(new deferred_response<std::atomic<int> >(test_callback, closure_data, "cycle callback response"));
58+
}
59+
};
60+
61+
int main(int argc, char** argv) {
62+
webserver ws = create_webserver(8080);
63+
64+
deferred_resource hwr;
65+
ws.register_resource("/hello", &hwr);
66+
ws.start(true);
67+
68+
return 0;
69+
}
70+

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: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,44 +28,11 @@ namespace httpserver
2828
namespace details
2929
{
3030

31-
ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max)
31+
MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t))
3232
{
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()
45-
{
46-
if(!completed)
47-
{
48-
return MHD_create_response_from_callback(
49-
MHD_SIZE_UNKNOWN,
50-
1024,
51-
&details::cb,
52-
this,
53-
NULL
54-
);
55-
}
56-
else
57-
{
58-
return static_cast<string_response*>(this)->get_raw_response();
59-
}
33+
return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL);
6034
}
6135

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

7038
}
71-

src/httpserver/deferred_response.hpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,44 +25,45 @@
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, 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),
51-
completed(false)
52+
closure_data(closure_data)
5253
{
5354
}
5455

5556
deferred_response(const deferred_response& other):
5657
string_response(other),
5758
cycle_callback(other.cycle_callback),
58-
completed(other.completed)
59+
closure_data(other.closure_data)
5960
{
6061
}
6162

6263
deferred_response(deferred_response&& other) noexcept:
6364
string_response(std::move(other)),
6465
cycle_callback(std::move(other.cycle_callback)),
65-
completed(other.completed)
66+
closure_data(std::move(other.closure_data))
6667
{
6768
}
6869

@@ -72,7 +73,7 @@ class deferred_response : public string_response
7273

7374
(string_response&) (*this) = b;
7475
this->cycle_callback = b.cycle_callback;
75-
this->completed = b.completed;
76+
this->closure_data = b.closure_data;
7677

7778
return *this;
7879
}
@@ -83,7 +84,7 @@ class deferred_response : public string_response
8384

8485
(string_response&) (*this) = std::move(b);
8586
this->cycle_callback = std::move(b.cycle_callback);
86-
this->completed = b.completed;
87+
this->closure_data = std::move(b.closure_data);
8788

8889
return *this;
8990
}
@@ -92,13 +93,20 @@ class deferred_response : public string_response
9293
{
9394
}
9495

95-
MHD_Response* get_raw_response();
96-
void decorate_response(MHD_Response* response);
96+
MHD_Response* get_raw_response()
97+
{
98+
return details::get_raw_response_helper((void*) this, &(this->cb));
99+
}
100+
97101
private:
98-
cycle_callback_ptr cycle_callback;
99-
bool completed;
102+
ssize_t (*cycle_callback)(std::shared_ptr<T>, char*, size_t);
103+
std::shared_ptr<T> closure_data;
100104

101-
friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max);
105+
static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max)
106+
{
107+
deferred_response<T>* dfr = static_cast<deferred_response<T>*>(cls);
108+
return dfr->cycle_callback(dfr->closure_data, buf, max);
109+
}
102110
};
103111

104112
}

0 commit comments

Comments
 (0)