Giter Site home page Giter Site logo

pistacheio / pistache Goto Github PK

View Code? Open in Web Editor NEW
3.1K 114.0 666.0 5.72 MB

A high-performance REST toolkit written in C++

Home Page: https://pistacheio.github.io/pistache/

License: Apache License 2.0

CMake 0.71% C++ 97.00% CSS 0.28% JavaScript 0.51% C 0.10% Shell 0.37% Meson 1.03%
server api-rest

pistache's Introduction

Pistache

N|Solid

linux autopkgtest codecov REUSE status

Pistache is a modern and elegant HTTP and REST framework for C++. It is entirely written in pure-C++17* and provides a clear and pleasant API.

Documentation

We are still looking for a volunteer to document fully the API. In the mean time, partial documentation is available at pistacheio.github.io/pistache/. If you are interested in helping with this, please open an issue ticket.

A benchmark comparison of Pistache to other C++ RESTful APIs was created by guteksan and is available here.

Articles, Tutorials & Videos

Dependencies

Pistache has the following third party dependencies

Contributing

Pistache is released under the Apache License 2.0. Contributors are welcome!

Pistache was originally created by Mathieu Stefani (@octal). He continues to contribute to the maintainence and development of Pistache, supported by a team of volunteers. The maintainers can be reached in #pistache on Libera.Chat (ircs://irc.libera.chat:6697). Please come and join us!

The Launchpad Team administers the daily and stable Ubuntu pre-compiled packages.

Versioning

The version of the library's public interface (ABI) is not the same as the release version, but we choose to always guarantee that the major release version and the soname version will match. The interface version is primarily associated with the external interface of the library. Different platforms handle this differently, such as AIX, GNU/Linux, and Solaris.

GNU Libtool abstracts each platform's idiosyncrasies away because it is more portable than using ar(1) or ranlib(1) directly. However, it is not supported in Meson so we made do without it by setting the SONAME directly.

When Pistache is installed it will normally ship:

  • libpistache.so.X.Y.Z: This is the actual shared-library binary file. The X, Y and Z values are the major, minor and patch interface versions respectively.

  • libpistache.so.X: This is the soname soft link that points to the binary file. It is what other programs and other libraries reference internally. You should never need to directly reference this file in your build environment.

  • libpistache.so: This is the linker name entry. This is also a soft link that refers to the soname with the highest major interface version. This linker name is what is referred to on the linker command line.

  • libpistache.a: This is the static archive form of the library. Since when using a static library all of its symbols are normally resolved before runtime, an interface version in the filename is unnecessary.

If your contribution has modified the interface, you may need to update the major or minor interface versions. Otherwise user applications and build environments will eventually break. This is because they will attempt to link against an incorrect version of the library -- or worse, link correctly but with undefined runtime behaviour.

The major version should be incremented when you make incompatible API or ABI changes. The minor version should be incremented when you add functionality in a backwards compatible manner. The patch version should be incremented when you make backwards compatible bug fixes. This can be done by modifying version.txt accordingly. Also remember to always update the commit date in the aformentioned file.

Precompiled Packages

If you have no need to modify the Pistache source, you are strongly recommended to use precompiled packages for your distribution. This will save you time.

Debian and Ubuntu

Pistache is available in the official repositories since Debian 12 and Ubuntu 23.10, under the package name libpistache-dev.

Supported Architectures

Currently Pistache is built and tested on a number of architectures. Some of these are suitable for desktop or server use and others for embedded environments. As of this writing we do not currently have any MIPS related packages that have been either built or tested.

  • amd64
  • arm64
  • armhf
  • i386
  • ppc64el
  • riscv64
  • s390x

Ubuntu PPA (Unstable)

The project builds daily unstable snapshots in a separate unstable PPA. To use it, run the following:

$ sudo add-apt-repository ppa:pistache+team/unstable
$ sudo apt update
$ sudo apt install libpistache-dev

Ubuntu PPA (Stable)

Currently there are no stable release of Pistache published into the stable PPA. However, when that time comes, run the following to install a stable package:

$ sudo add-apt-repository ppa:pistache+team/stable
$ sudo apt update
$ sudo apt install libpistache-dev

Other Distributions

Package maintainers, please insert instructions for users to install pre-compiled packages from your respective repositories here.

Use via pkg-config

If you would like to automatically have your project's build environment use the appropriate compiler and linker build flags, pkg-config can greatly simplify things. It is the portable international de facto standard for determining build flags. The development packages include a pkg-config manifest.

GNU Autotools

To use with the GNU Autotools, as an example, include the following snippet in your project's configure.ac:

# Pistache...
PKG_CHECK_MODULES(
    [libpistache], [libpistache >= 0.0.2], [],
    [AC_MSG_ERROR([libpistache >= 0.0.2 missing...])])
YOURPROJECT_CXXFLAGS="$YOURPROJECT_CXXFLAGS $libpistache_CFLAGS"
YOURPROJECT_LIBS="$YOURPROJECT_LIBS $libpistache_LIBS"

Meson

To use with Meson, you just need to add dependency('libpistache') as a dependency for your executable.

project(
    'MyPistacheProject',
    'cpp',
    meson_version: '>=0.55.0'
)

executable(
    'MyPistacheExecutable',
    sources: 'main.cpp',
    dependencies: dependency('libpistache')
)

If you want to build the library from source in case the dependency is not found on the system, you can add this repository as a submodule in the subprojects directory of your project, and edit the dependency() call as follows:

dependency('libpistache', fallback: 'pistache')

If you're using a Meson version older than 0.55.0 you'll have to use the "older" syntax for dependency():

dependency('libpistache', fallback: ['pistache', 'pistache_dep'])

Lastly, if you'd like to build the fallback as a static library you can specify it with the default_options keyword:

dependency('libpistache', fallback: 'pistache', default_options: 'default_library=static')

CMake

To use with a CMake build environment, use the FindPkgConfig module. Here is an example:

cmake_minimum_required(VERSION 3.6)
project("MyPistacheProject")

find_package(PkgConfig)
pkg_check_modules(Pistache REQUIRED IMPORTED_TARGET libpistache)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PkgConfig::Pistache)

Makefile

To use within a vanilla makefile, you can call pkg-config directly to supply compiler and linker flags using shell substitution.

CFLAGS=-g3 -Wall -Wextra -Werror ...
LDFLAGS=-lfoo ...
...
CFLAGS+= $(pkg-config --cflags libpistache)
LDFLAGS+= $(pkg-config --libs libpistache)

Building from source

To download the latest available release, clone the repository over GitHub.

$ git clone https://github.com/pistacheio/pistache.git

Now, compile the sources:

$ cd pistache
$ meson setup build                                 \
    --buildtype=release                             \
    -DPISTACHE_USE_SSL=true                         \
    -DPISTACHE_BUILD_EXAMPLES=true                  \
    -DPISTACHE_BUILD_TESTS=true                     \
    -DPISTACHE_BUILD_DOCS=false                     \
    -DPISTACHE_USE_CONTENT_ENCODING_BROTLI=true     \
    -DPISTACHE_USE_CONTENT_ENCODING_DEFLATE=true    \
    --prefix="$PWD/prefix"
$ meson compile -C build
$ meson install -C build

Optionally, you can also run the tests. You can skip tests requiring network access with --no-suite=network:

$ meson test -C build

Be patient, async_test can take some time before completing. And that's it, now you can start playing with your newly installed Pistache framework.

Some other Meson options:

Option Default Description
PISTACHE_USE_SSL False Build server with SSL support
PISTACHE_BUILD_TESTS False Build all of the unit tests
PISTACHE_BUILD_EXAMPLES False Build all of the example apps
PISTACHE_BUILD_DOCS False Build Doxygen docs
PISTACHE_USE_CONTENT_ENCODING_BROTLI False Build with Brotli content encoding support
PISTACHE_USE_CONTENT_ENCODING_DEFLATE False Build with deflate content encoding support

Example

Hello World (server)

#include <pistache/endpoint.h>

using namespace Pistache;

struct HelloHandler : public Http::Handler {
  HTTP_PROTOTYPE(HelloHandler)
  void onRequest(const Http::Request&, Http::ResponseWriter writer) override {
    writer.send(Http::Code::Ok, "Hello, World!");
  }
};

int main() {
  Http::listenAndServe<HelloHandler>(Pistache::Address("*:9080"));
}

Tutorials

Project status

Pistache hasn't yet hit the 1.0 release. This means that the project is unstable but not unusable. In fact, most of the code is production ready; you can use Pistache to develop a RESTful API without issues, but the HTTP client has a few issues in it that make it buggy.

* While most code uses modern C++, Pistache makes use of some Linux-specific APIs where the standard library doesn't provide alternatives, and works only on that OS. See #6 for details. If you know how to help, please contribute a PR to add support for your desired platform :)

pistache's People

Contributors

adamburgess avatar adriancx avatar arthurafarias avatar bdvd avatar ciody avatar cyremur avatar dennisjenkins75 avatar dependabot[bot] avatar dgreatwood avatar ffontaine avatar fylax avatar hasankandemir1993 avatar hyperxor avatar iroddis avatar jenniferbuehler avatar jpihl avatar kiplingw avatar knowledge4igor avatar kuzkry avatar lcinacio avatar lillypad avatar mohsenomidi avatar mplellison avatar muttleyxd avatar ne02ptzero avatar oktal avatar snoozetime avatar tachi107 avatar yisaj avatar zydxhs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pistache's Issues

REST pistache server not always capturing REST requests

Hello,

I Wrote a REST Server based on Pistache, integrated in a multithreaded application, and the submitted REST requests should contain data structured as a google protocol buffers. Sometimes, the server captures the incoming requests and sometimes it not capturing.
Can anyone help me please ??

This is my code :

`***********************************
RESTHandler.hxx
#include "pistache/endpoint.h"
using namespace Net;
class RESTHandler : public Http::Handler
{
public:
RESTHandler();
~RESTHandler();
HTTP_PROTOTYPE(RESTHandler)
void onRequest(const Http::Request& request, Http::ResponseWriter response);
};

RESTHandler.cxx
#include "RESTHandler.hxx"
using namespace Net;
RESTHandler::RESTHandler() {}
RESTHandler::~RESTHandler() {}
void RESTHandler::onRequest(const Http::Request& request, Http::ResponseWriter response)
{
response.send(Http::Code::Accepted, "202 Accepted!! \n");
}

MyProgram.hxx
#include "RESTHandler.hxx"
using namespace Net;
class MyProgram
{
private:
static bool end;
public:
MyProgram();
~MyProgram();
static bool launchRestServer();
static void* restListener(void* params);
}

MyProgram.cxx

#include "MyProgram.hxx"
#include <boost/thread.hpp>
#include <boost/chrono.hpp>

MyProgram::end=false;

MyProgram::MyProgram()
{
launchRestServer();
}
MyProgram::~MyProgram()
{
end = true;
}

void* MyProgram::restListener(void* params)
{
std::shared_ptrNet::Http::Endpoint server = *((std::shared_ptrNet::Http::Endpoint *)params);
auto opts = Http::Endpoint::options().threads(1);
opts.flags(Tcp::Options::ReuseAddr);
server->init(opts);
server->setHandler(std::make_shared());
server->serveThreaded();
}

bool MyProgram::launchRestServer()
{
Net::Address addr(Net::Ipv4::any(), Net::Port(9080) );
std::shared_ptrNet::Http::Endpoint restServer(std::make_sharedNet::Http::Endpoint(addr));

cout << "Initializing a REST server on port " << restPort << endl;
boost::thread m_Thread= boost::thread(restListener, (void*)&restServer);
while (true)
{
if (end)
break;
}
restServer.shutdown();
return true;
}

Socket file descriptor leak in client, and possibly other FD leaks as well

There are more problems with file descriptors besides issue #38. This may be an indication that the handling of FDs as a whole should be revised.

If I modify the http_client example to sleep for a while after client.shutdown(), and I do an ls -l /proc/$pid/fd/ with the $pid of the example binary, I see one additional ‘socket’ FD per request that was made (try running it with one request and 20, and compare). These FDs should have been cleaned up after the client shutdown.

In a more complex program that uses both the server and client parts of Pistache to perform a second REST call in response to an incoming call, it is worse. This program has the same basic structure as the rest_server example, but inside its Route::Handler, it determines what endpoint to contact based on an identifier in the call. It then creates a Http::Client, and uses Async::Barrier to wait for the response before doing the send() on the original request (this part is very similar to the http_client example).
Every time this program has handled an incoming request, five FDs are leaked:

anon_inode:[eventpoll]
anon_inode:[eventfd]
anon_inode:[eventfd]
anon_inode:[eventfd]
socket:[5748722]

This program is supposed to run continuously for possibly a year or more. That is not going to be possible with such a leak.
I have attached a modified version of rest_server.cc that reproduces this leak. Invoke it with some HTTP or REST server URL as third argument, and do a request on the /forward path. The URL does not need to point to a working server to trigger the leak. (Even better: if the URL is inacessible, you will experience issue #37. Also try entering a random string as third argument and see what happens, and whether you can modify the program to catch this failure.)

rest_server_and_client.cc.txt

The leaks are not limited to the client, another server-only program leaks 5 sockets per REST call. However, we haven't found yet why this program exhibits this leak while the example doesn't.

Async access to redis

Hi, i would like to know if there is a way to access a redis DB in an async way. I would like to use hiredis in for this. Is there a simple way of doing this ?

Custom Clone(obj*) method

Hello,
Currently I need a clone with this signature.

std::shared_ptr<Net::Tcp::Handler> clone(Uservice_Interface* usvc) const {
       return std::make_shared<Endpoint>(usvc) ;
   } typedef Net::Http::details::prototype_tag tag;

But, it does not work as clone() is required for a http_prototype,
Is it possible to specify a custom clone in a Http Handler ?.
My current issue is that I need to pass parameters to the constructor of my http handler class.

FD leak in streamFile

There is lead of the file descriptor used to check existence using fstat. Similarly the fd used to read the file is not closed. This is critical issue bringing down the system.

run with valgrind --tool=memcheck --track-fds=yes ... to check.

Compile error on macOS Sierra 10.12.2

I'm geting the following compile error:

served/pistache/include/os.h:42:5: error: unknown type name
'cpu_set_t'
cpu_set_t toPosix() const;

thanks

brian

Building pistache for ARM

Hello!

Thank you for this great library! As reading the documents looks like it is very well designed and looks a lot like the ASP.Net WebAPIs in C# world.

So, we are trying to build the library for one of our ARM devices and this is the output:

guto@GUTO-SURFACE3:/mnt/c/Dev/Linux/pistache/build$ cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ..
-- The C compiler identification is GNU 5.2.0
-- The CXX compiler identification is GNU 5.2.0
-- Check for working C compiler: /mnt/c/Dev/Linux/toolchain/bin/arm-arm1176jzs-linux-gnueabi-gcc
-- Check for working C compiler: /mnt/c/Dev/Linux/toolchain/bin/arm-arm1176jzs-linux-gnueabi-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /mnt/c/Dev/Linux/toolchain/bin/arm-arm1176jzs-linux-gnueabi-g++
-- Check for working CXX compiler: /mnt/c/Dev/Linux/toolchain/bin/arm-arm1176jzs-linux-gnueabi-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Performing Test COMPILER_SUPPORTS_CXX11
-- Performing Test COMPILER_SUPPORTS_CXX11 - Success
-- Performing Test COMPILER_SUPPORTS_CXX0X
-- Performing Test COMPILER_SUPPORTS_CXX0X - Success
CMake Warning at examples/CMakeLists.txt:16 (find_package):
  By not providing "FindRapidJSON.cmake" in CMAKE_MODULE_PATH this project
  has asked CMake to find a package configuration file provided by
  "RapidJSON", but CMake did not find one.

  Could not find a package configuration file provided by "RapidJSON" with
  any of the following names:

    RapidJSONConfig.cmake
    rapidjson-config.cmake

  Add the installation prefix of "RapidJSON" to CMAKE_PREFIX_PATH or set
  "RapidJSON_DIR" to a directory containing one of the above files.  If
  "RapidJSON" provides a separate development package or SDK, be sure it has
  been installed.


-- Could NOT find GTest (missing:  GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY)
-- Found PythonInterp: /usr/bin/python (found version "2.7.6")
-- Looking for include file pthread.h
-- Looking for include file pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/c/Dev/Linux/pistache/build
guto@GUTO-SURFACE3:/mnt/c/Dev/Linux/pistache/build$ ls
CMakeCache.txt  cmake_install.cmake  examples                  Makefile  tests
CMakeFiles      CTestTestfile.cmake  googletest-release-1.7.0  src

The complaining part is CMake trying to find RapidJSON. The docs say that Pistache has no dependency... Do we need to ignore that warning or should we build RapidJSON?

Also, I saw that you have interest in implement WebSocket in it, I would love to use and test it.

Thanks!

example compilation issue Ubuntu 16.04

Hi,

I'm having troubles to compile the hello world example below:

#include <pistache/endpoint.h>

using namespace Net;

struct HelloHandler : public Http::Handler {
    void onRequest(const Http::Request& request, Http::ResponseWriter writer) {
                writer.send(Http::Code::Ok, "Hello, World!");

    }

};

int main() {
        Http::listenAndServe<HelloHandler>("*:9080");

}

compilation: gcc -std=c++11 hello.cc

In file included from /usr/local/include/pistache/endpoint.h:11:0,
                 from hello.cc:1:
/usr/local/include/pistache/http.h: In instantiation of ‘std::shared_ptr<_Tp1> Net::Http::make_handler(Args&& ...) [with H = HelloHandler; Args = {}]’:
/usr/local/include/pistache/endpoint.h:93:46:   required from ‘void Net::Http::listenAndServe(Net::Address, const Net::Http::Endpoint::Options&) [with Handler = HelloHandler]’
/usr/local/include/pistache/endpoint.h:85:28:   required from ‘void Net::Http::listenAndServe(Net::Address) [with Handler = HelloHandler]’
hello.cc:14:52:   required from here
/usr/local/include/pistache/http.h:758:5: error: static assertion failed: An http handler must be an http prototype, did you forget the HTTP_PROTOTYPE macro ?
     static_assert(details::IsHttpPrototype<H>::value, "An http handler must be an http prototype, did you forget the HTTP_PROTOTYPE macro ?");

gcc: 4:5.3.1-1ubuntu1

thanks for your help !

Pistache work on mac?

Very nice project, but i was unable to install and use it on a mac (osx 10.11.x).

Documentation Error

Hello this line is wrong in your documentation: Http::Router router under "Defining Routes". It should be Net::Rest::Router router. I think you could be more explicit in the documentation & examples: don't use namespaces and tell the user the proper includes. Nice library, thanks.

Consider making header names case insensitive

When posting a content-type header, pistache endpoint only correctly parses the header when "Content-Type" is capitalized. I am not sure about standards, but when I ran a package analysis on Swagger UI example requests http://petstore.swagger.io/, I noticed that they use "content-type" not capitalized.

I think request.headers().get<Http::Header::ContentType>() should accept both "Content-Type" and "content-type". Currently, if you use curl localhost -X POST -H "Content-Type: application/json" and look at the mime(), it is correctly recognized as application/json. Using the non-capitalized version however, it throws Could not find header.

I understand this issue might be up for discussion, but I believe being more lenient on capitalization is the more graceful thing to do here, especially if there are other finished products that you may want to use in conjunction with pistache.io and use "content-type" in their framework, since that case will make debugging and fixing the problem for "end-user" developers rather tedious.

Better handling of connection failures in client

There is no way to explicitly catch connection failures in client code. The implementation in client/client.cc intercepts exceptions during connect, by means of its ‘ExceptionPrinter’. This prints the exception on std::cout and leaves the PromiseHttp::Response in a limbo state, meaning the calling program cannot know what exactly went wrong. It can only rely on a default error state and a timeout.

For instance, if I run the http_client.cc example with a resource path starting with a local IP address that is serving nothing (e.g. http://127.0.0.1:4321). It will print: “Got exception: Could not connect: Resource temporarily unavailable” and then wait for the 5-second barrier timeout even though it is obvious that the request has already failed.
I have tried modifying the example by defining other handlers than Async::IgnoreException, but it makes no difference. I cannot manage to trigger a lambda that takes an exception_ptr, it is never invoked.

[BUG] Broken HTTP response

Some responses (seems arbitrary. Works until 25 Kb, but not beyond that) produce a broken HTTP response, where the response body is sent partially and new headers (500) are printed right after the body.

Host

Ubuntu 16.04
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

Sample program.

void testHandler(const Net::Rest::Request &request,
	                   Net::Http::ResponseWriter response) {
	std::string out = "";

	for(int n=0; n<6000; n++) {
		out += "xxxxxxxxxx";
	}

	response->send(Net::Http::Code::Ok, out);
}

int main(int argc, char **argv, char **envp) {
	Net::Rest::Router router;

	Net::Rest::Routes::Get(router, "/",
	                     Net::Rest::Routes::bind(&testHandler));

	Net::Address addr(Net::Ipv4::any(), Net::Port(8000));
		auto opts = Net::Http::Endpoint::options().threads(10).flags(
					Net::Tcp::Options::ReuseAddr);

	Net::Http::Endpoint server(addr);
	server.init(opts);
	server.setHandler(router.handler());
	server.serve();

	return 0;
}

Request

curl -I http://127.0.0.1:8000

Response

HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Length: 60000

xxxxx ...... xxxxxHTTP/1.1 500 Internal Server Error
Connection: Keep-Alive
Content-Length: 21

Accessing POST parameters?

request.body() gives the raw request body, but is there any way to access POST parameters (like URI parameters) directly? Does the library not past the POST body into a map or something?

feature

would be great to have macos,ios support as epoll.h is not supported

the api is great anyway.

regards
david

Async access to data in Handler

Dear all,

I need to write a REST server where the data I need to server is accessible in a async manner (send a message to a service and then receive a message from this service)

Is this possible with Pistache? And if so, can you please provide me with an example?

Thanks,

Pablo.

Graceful Shutdown?

Is there a way to implement a "/shutdown" endpoint with pistache?

In the examples, I noticed that Http::Endpoint.shutdown() is called after Http::Endpoint.serve(). How would it ever get to the the shutdown() function? [At the moment, to stop the service, I just Ctrl+C it]

Apache benchmark fails after 1 request

Hi

I just wanted to test the performance of "rest_server". However apache benchmark just sends one request and then stalls.

bo@alpha:~/tmp/pistache> ab -v4 -c 1 -n 100 http://127.0.0.1:9080/ready/
This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)...INFO: GET header == 
---
GET /ready/ HTTP/1.0
Host: 127.0.0.1:9080
User-Agent: ApacheBench/2.3
Accept: */*


---
LOG: header received:
HTTP/1.0 200 OK
Connection: Keep-Alive
Content-Length: 1

1
LOG: Response code = 200
^C

Server Software:        
Server Hostname:        127.0.0.1
Server Port:            9080

Document Path:          /ready/
Document Length:        0 bytes

Concurrency Level:      1
Time taken for tests:   0.908 seconds
Complete requests:      0
Failed requests:        0
Total transferred:      63 bytes
HTML transferred:       1 bytes

Compiling with pistache.io

I did not see how to compile pistache.io examples. The compiling threw following error
In file included from /usr/include/x86_64-linux-gnu/c++/5/bits/c++allocator.h:33:0, from /usr/include/c++/5/bits/allocator.h:46, from /usr/include/c++/5/string:41, from /usr/include/c++/5/bits/locale_classes.h:40, from /usr/include/c++/5/bits/ios_base.h:41, from /usr/include/c++/5/ios:42, from /usr/include/c++/5/ostream:38, from /usr/include/c++/5/iostream:39, from /srv/Workspaces/nithu/FrontPage/register_service/src/RegisterServiceServlet.hh:9, from /srv/Workspaces/nithu/FrontPage/register_service/src/RegisterServiceServlet.cc:8: /usr/include/c++/5/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = RegisterServiceServlet; _Args = {}; _Tp = RegisterServiceServlet]’: /usr/include/c++/5/bits/alloc_traits.h:530:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = RegisterServiceServlet; _Args = {}; _Tp = RegisterServiceServlet; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<RegisterServiceServlet>]’ /usr/include/c++/5/bits/shared_ptr_base.h:522:39: required from ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {}; _Tp = RegisterServiceServlet; _Alloc = std::allocator<RegisterServiceServlet>; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’ /usr/include/c++/5/bits/shared_ptr_base.h:617:4: required from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = RegisterServiceServlet; _Alloc = std::allocator<RegisterServiceServlet>; _Args = {}; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’ /usr/include/c++/5/bits/shared_ptr_base.h:1097:35: required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<RegisterServiceServlet>; _Args = {}; _Tp = RegisterServiceServlet; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’ /usr/include/c++/5/bits/shared_ptr.h:319:64: required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<RegisterServiceServlet>; _Args = {}; _Tp = RegisterServiceServlet]’ /usr/include/c++/5/bits/shared_ptr.h:620:39: required from ‘std::shared_ptr<_Tp1> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = RegisterServiceServlet; _Alloc = std::allocator<RegisterServiceServlet>; _Args = {}]’ /usr/include/c++/5/bits/shared_ptr.h:635:39: required from ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = RegisterServiceServlet; _Args = {}]’ /srv/Workspaces/nithu/FrontPage/register_service/src/RegisterServiceServlet.hh:23:3: required from here /usr/include/c++/5/ext/new_allocator.h:120:4: error: invalid new-expression of abstract class type ‘RegisterServiceServlet’ { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } ^ compilation terminated due to -Wfatal-errors. CMakeFiles/consoleTest_Register_Service.dir/build.make:65: recipe for target 'CMakeFiles/consoleTest_Register_Service.dir/src/RegisterServiceServlet.cc.o' failed make[2]: *** [CMakeFiles/consoleTest_Register_Service.dir/src/RegisterServiceServlet.cc.o] Error 1 make[2]: Leaving directory '/srv/Workspaces/nithu/FrontPage/register_service/build' CMakeFiles/Makefile2:70: recipe for target 'CMakeFiles/consoleTest_Register_Service.dir/all' failed make[1]: *** [CMakeFiles/consoleTest_Register_Service.dir/all] Error 2 make[1]: Leaving directory '/srv/Workspaces/nithu/FrontPage/register_service/build' Makefile:86: recipe for target 'all' failed make: *** [all] Error 2 $

Server threads hang up on large responses

I'm not sure if I'm doing something wrong. I've tried I get bad results when sending large responses. The exact threshold isn't consistent but seems to be consistent given the same response message. In my actual application, the problem threshold seems to be around 750-1000kb. I constructed a test case and it seems to handle 1mb just fine but 10mb produces the same result.

The behavior I see is that large files are sent -- sometimes successfully, sometimes not. This appears to leave the servicing thread in a hung state. After doing this n-1 times, where n is the number of threads passed into Net::Http::Endpoint::Init(), all further requests to the server hang with no response. I see no exception output from the server indicating any problems.

The attached code demonstrates it using Net::Http::serveFile(), but I observe the same result in large string results that I construct in code and pass into Net::Http::ResponseWriter::send().

To reproduce:
Test: demo.zip

# cmake . 
# make
# ./demo &
# curl "http://127.0.0.1:8088/1e7" >/dev/null
# curl "http://127.0.0.1:8088/1e7" >/dev/null
# curl "http://127.0.0.1:8088/1e7" >/dev/null

The makefile will produce four dummy files for testing: 10k, 100k, 1m, and 10m.

Swagger support?

I see the stub in the documentation for Swagger support - is there a way to get it working?

"Bad file descriptor error"

I'm trying to run the example files (and they run sometimes), but I get this error intermittently. Any ideas? Many thanks.

$ examples/http_server 9500 2
Cores = 4
Using 2 threads
terminate called after throwing an instance of 'std::runtime_error'
  what():  epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev): Bad file descriptor
[1]    12311 abort (core dumped)  examples/http_server 8000 2

Websockets support

Hello, does this library implement websockets? and how mature this library? can it be used for production? Impressive library btw.

Also, I am writing my own project at the moment in golang, and I was thinking to rewrite to c++ using this library and probably due these reasons: less dependencies, more performant? full control over memory, also wanted to learn more c++ (at least thats what i am thinking), and the question is "is it worth it?" ))

Performance Benchmarks

Hello,

Found this repo through Pistache.io. Being "performance-focused" as indicated in the site, I am more interested on the actual speed (e.g. round-trip calls per second). Did anyone tried to benchmark it?

Thanks.

Expose Router::addRoute()?

@oktal What do you think of exposing Router::addRoute()? This'll make adding routes dynamically easier without the need for defining a handler class, or using the ::Get ... ::Post etc. methods. For example, if I have a map of routes+methods => handler references, it'd be possible to just loop through them and add them on the go.

Request to update header files to better prevent namespace and header file name collisions

This is a cool project, I like it!

Some minor requests if not already done or work in progress:

  • Change the base namespace from "Net" to "pistache". Likely it would better prevent namespace
    collision with another project using "Net".
  • In the header files change the includes for Pistache files to be '#include "pistache/..." This will likely
    prevent the include file name collisions if other projects happen to have a similar name.
  • Renaming the libraries from libnet to libpistache would prevent potential library file name collisions..
  • Add "-Wall" and "-Werror" to the compilation so that any warnings / errors can be identified and
    resolved.
  • In example it could, arguably, be better so that all Pistache type names are fully qualified, or an
    alias be used.

accepting POST data larger than 1024

I seem to be having a problem when the server is getting POST data that is long.
Using the example http_server and sending:
curl 0.0.0.0:9080/echo -H "Content-Type: application/xml" -d @sample.xml

the server will echo back if the size of sample.xml is less than 1024. Bigger and it just hangs, no response. I changed the test http_server to cout the length of body() before doing the echo response and it doesnt output anything on these large sets, implying to me that the handler is not even called.

undefined reference to Net::Http::Endpoint::bind

Hi,
I am new in using Pistache. I installed the latest version on a Debian 8.2 Jessie, and want to create a Rest server in a c++ application.
I followed the "Getting started", but i defined in a class a static member restServer of type Http::Endpoint, then i bound this member with an address (using the bind method).
This is my code :

          ` static Http::Endpoint restServer;

            restServer.bind(addr);
	restServer.init(opts);
	restServer.setHandler(std::make_shared<RESTHandler>());
	restServer.serve();`

I added in my makefile the -lnet option so that to link with libnet library that i found installed after pistache installation. But, i got an error when linking :
undefined reference to Net::Http::Endpoint::bind.
I searched in endpoint.h and see that it defines the bind method, so what is the problem ?

Without using the bind method, it works well, but i need to use the static member so that i can stop the rest server whenever i want.

Can anyone help me please ??

Thank you !

Json in query doesn't work

I was trying the client method, it works with normal url and query.

eg: localhost:8083/myservice?data=xyz

But if I give json value to data in the query, it does not generate a request.

Should I add any json parser before constructing the query string? Please let me know, what I am missing

Featurerequest: Add a 'precondition' to Router.

While using Pistache, I often use the same common code in every Handler for every route.
Something like (Attention: Mostly pseudocode, but you get the idea)

response.headers().add(/*Contenttype*/);
ownObject* oo = new ownObject();
if(oo->isAlive())
{
  // Actual Work for Handler
  // Send and Return if successfull
}
oo->foo = 1;
oo->bar = "Service Unavailable";
response.send(Http::Code::Service_Unavailable,oo->dump());
delete oo;

Wouldn't it be nice to add a function to the Router that gets called before the actual handler kicks in?

Router->addPreworker(/*ptrToFunction*/)

Preworkerfunction:

void preFunc(Router, request, response){     //or only request, response
  //do stuff
  if(!router->for(request)->callHandler()) {   //and do something like request->callHandler()
    //handler would be bool instead of void and return true when everything went fine
    //and false if a common error occurs

    //do errorreport-generation
    response.send(Http::Code::Service_Unavailable,errorreport);
  }
}

Wouldn't be this a nice pattern to reduce code and points of failure?
Maybe I implement this by myself when I find some time.

C++03 support

Hi,
The library seems very powerful. I was just wondering why the use of C++11?
Do you think it would it be a huge effort to support C++03?
And why not compiling my app with a C++11 compiler: because of industrial maintainability, standard dev toolsets shipped with Centos 6 for example.
Thanks!

rest_server.cc example issue

no compile errors in rest_server.cc example but the example crashes on run and return munmap_chunk(): invalid pointer: 0x00007ffdf9f23ff0 *** error

CORS Example?

Hello,

I was wondering if there was an example of Pistache supporting a CORS return header or if that could be created.  I have attempted to work through the header examples on the site, but haven't been successful in generating that header. 

Cheers,

Cookie ghost reads.

Hello.

I have a weird problem. After setting and then re-setting a cookie, my pistache REST server seems to work with the previous value of the cookie. This occurs only after the initial setting of the cookie, that is it works fine one time, then if you overwrite the cookie, Pistache doesn't seem to recognize the change until
a very long (~2-5 minutes) delay or a server restart.

I have come to the conclusion that Pistache is somehow storing the cookie values internally.
They are not transferred incorrectly. I have monitored the HTTP traffic, and the cookies are correctly displayed in the request. Furthermore, if you delete the cookie by setting a low max-age (very inelegant solution but whatever) , the request will not contain the cookie, but the server still reads the outdated shadow-value!

Example: (after logout, that is the cookie has been set and read by the server at least once, but notice that its not present in the request)

`127.000.000.001.38984-127.000.000.001.03000: GET /worksheets HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive`

Then the server prints the cookies: JWTtoken=value_of_a_valid_token

What is happening, please help.

Here is a chunk of code that should encompass this problem:

RouteHandler.h - where i define the custom router

#include <pistache/router.h>
#include <Authenticator.h>
#include <iDatabaseSource.h>
#include <Utils.h>
#include <Results.h>

class RouteHandler {
    public:
        RouteHandler(Net::Rest::Router & router,iDatabaseSource & database);

        void serveApp(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void serveIndex(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void serveImage(const Net::Rest::Request& request, Net::Http::ResponseWriter response);

        void doLogin(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void doLogout(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void doRegister(const Net::Rest::Request& request, Net::Http::ResponseWriter response);

        void getWorksheet(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void selectWorksheet(const Net::Rest::Request& request, Net::Http::ResponseWriter response);
        void getWorksheets(const Net::Rest::Request& request, Net::Http::ResponseWriter response);

        void testHandler(const Net::Rest::Request& request, Net::Http::ResponseWriter response);

    private:
        Authenticator auth;
        iDatabaseSource & database;
};

RouteHandler.cpp - the implementation of the custom router

#include <RouteHandler.h>

RouteHandler::RouteHandler(Net::Rest::Router &router, iDatabaseSource &database) : database(database)
{
    Net::Rest::Routes::Post(router, "/login", Net::Rest::Routes::bind(&RouteHandler::doLogin, this));
    Net::Rest::Routes::Get(router, "/logout", Net::Rest::Routes::bind(&RouteHandler::doLogout, this));
    Net::Rest::Routes::Post(router, "/register", Net::Rest::Routes::bind(&RouteHandler::doRegister, this));

    Net::Rest::Routes::Get(router, "/", Net::Rest::Routes::bind(&RouteHandler::serveIndex, this));
    Net::Rest::Routes::Get(router, "/dist/scripts/main.js", Net::Rest::Routes::bind(&RouteHandler::serveApp, this));
    Net::Rest::Routes::Get(router, "/src/img/*", Net::Rest::Routes::bind(&RouteHandler::serveImage, this));

    Net::Rest::Routes::Get(router, "/users/:id/worksheet", Net::Rest::Routes::bind(&RouteHandler::getWorksheet, this));
    Net::Rest::Routes::Post(router, "/users/:id/SelectWorksheet", Net::Rest::Routes::bind(&RouteHandler::selectWorksheet, this));
    Net::Rest::Routes::Get(router, "/worksheets", Net::Rest::Routes::bind(&RouteHandler::getWorksheets, this));

}

//accepts a json object with credentials, then looks up an user with these. If an user is found, authenticates the requester with a JWT set in a cookie.
void RouteHandler::doLogin(const Net::Rest::Request &request, Net::Http::ResponseWriter response)
{
    auto ct = request.headers().get<Net::Http::Header::Host>();

    auto json = Utils::decodeSimpleJSON(request.body());

    std::string username;
    std::string password;
    try
    {
        username = json.at("username");
        password = json.at("password");
    }
    catch (std::exception e)
    {
        std::cerr << e.what() << " Error in credentials" << std::endl;
        response.send(Net::Http::Code::Unauthorized);
        return;
    }
    Models::User user = database.getUserByCredentials(username, password);
    if (user.isValid())
    {
        std::map<std::string, std::string> userData;
        userData.insert({"displayName", user.getDisplayName()});
        userData.insert({"id", std::to_string(user.getID())});

        std::cout << "login from " << ct->host() << ":" << ct->port() << " - " << request.body() << std::endl;
        std::string token = auth.generateToken(user);
        std::string payload("JWTtoken=");
        payload.append(token);
        payload.append("; path=/ ");
        payload.append("; domain=localhost ");
        payload.append("; Max-Age=3600");
        Net::Http::Cookie cookie = Net::Http::Cookie::fromString(payload);
        cookie.httpOnly = true;
        response.cookies().add(cookie);
        //userData is sent back to be stored in JavaScript
        response.send(Net::Http::Code::Ok, Utils::makeSimpleJSON(userData));
        return;
    }
    else
    {
        std::cout << "INVALID login from " << ct->host() << ":" << ct->port() << " - " << request.body() << std::endl;
        response.send(Net::Http::Code::Unauthorized);
        return;
    }
    response.send(Net::Http::Code::Internal_Server_Error);
}

//logs the requester out by setting the JWT to an invalid value and setting the expiration low so that the browser will delete the cookie. Which it does, requests after logout DO NOT contain a JWT cookie!
void RouteHandler::doLogout(const Net::Rest::Request &request, Net::Http::ResponseWriter response)
{
    auto cookieJar = request.cookies();
    if (cookieJar.has("JWTtoken"))
    {
        auto cookie = request.cookies().get("JWTtoken");
        cookie = Net::Http::Cookie::fromString("JWTtoken=Logged Out.; path=/ ; domain=localhost ; max-age=2");
        cookie.httpOnly = true;
        response.cookies().add(cookie);
    }
    response.send(Net::Http::Code::Ok);
}

//Returns a JSON array of data, IF the user is authorized.
void RouteHandler::getWorksheets(const Net::Rest::Request &request, Net::Http::ResponseWriter response)
{
    auto cookies = request.cookies();
    if (!cookies.has("JWTtoken"))
    {
        std::cout << "No cookie" << std::endl;
        response.send(Net::Http::Code::Forbidden);
        return;
    }
    //Print the cookies
    //Here is where the ghost reads are visible.
    for (const Net::Http::Cookie &cookie : cookies)
    {
        std::cout << "Key: " << cookie.name << ", value: " << cookie.value << std::endl;
        std::cout << "Raw cookie: "<<std::endl;
        cookie.write(std::cout);
        std::cout << std::endl;
    }

    Net::Http::Cookie cookie = cookies.get("JWTtoken");
    std::string token = cookie.value;
    Models::User user(auth.authenticateUser(token));
    if (!user.isValid())
    {
        response.send(Net::Http::Code::Forbidden);
        return;
    }
    std::vector<Models::Worksheet> sheets = database.getAllWorksheets(Sort::None);

    std::string res("{\"sheets\":[");
    bool first = true;

    for (auto &sheet : sheets)
    {
        if (!first)
        {
            res.append(",");
        }
        res.append(sheet.toJSON());
        first = false;
    }
    res.append("]}");

    response.send(Net::Http::Code::Ok, res);
}

Some tested scenarios:

User hasn't logged in previously + tries to access protected resource -> response: forbidden (Good behaviour)

User has logged in for the first time -> response : receives JWT and is stored in cookie (Good behaviour)

User has logged in for the first time + tries to access protected resource -> response: OK, resource is served (Good behaviour)

User has logged in for the first time + logs out -> JWT gets overwritten and then deleted by browser (Good behaviour)

User has logged in for the first time + logs out + tries to access protected resource -> response: OK, resource is served (Undesired behaviour)

User has logged in for the first time + logs out + logs in again -> response : receives JWT and is stored in cookie (Good behaviour)
BUT Depending on time elapsed, upon requesting a protected resource the user can send his previous JWT, no JWT at all or the current JWT.

Thank you for your time.

Edit: Forgot to add the server setup, maybe the settings have something to do with this.

int main (int argc , char ** argv){
    Net::Address acceptFromAddress(Net::Ipv4::any(),Net::Port(3000));
    auto options = Net::Http::Endpoint::options().threads(10);
    Net::Rest::Router router;
    Mocks::DatabaseMock database;
    RouteHandler handler(router,database);
    Net::Http::Endpoint server(acceptFromAddress);
    server.init(options);
    server.setHandler(router.handler());
    server.serveThreaded();
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.