Commit 8ce1ed80 authored by Jeppe Pihl's avatar Jeppe Pihl

update to new master

parents bb1fa6ba 71846007
......@@ -4,6 +4,10 @@ include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra -Wno-missing-field-initializers")
option(PISTACHE_BUILD_TESTS "build tests alongside the project" OFF)
option(PISTACHE_BUILD_EXAMPLES "build examples alongside the project" OFF)
option(PISTACHE_INSTALL "add pistache as install target (recommended)" ON)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
......@@ -19,15 +23,19 @@ add_subdirectory (src)
include_directories (src)
add_subdirectory (examples)
find_package(GTest)
if (GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIRS})
else()
ADD_SUBDIRECTORY (googletest-release-1.7.0)
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
if (PISTACHE_BUILD_EXAMPLES)
add_subdirectory (examples)
endif()
enable_testing()
add_subdirectory(tests)
if (PISTACHE_BUILD_TESTS)
find_package(GTest)
if (GTEST_FOUND)
include_directories(${GTEST_INCLUDE_DIRS})
else()
ADD_SUBDIRECTORY (googletest-release-1.7.0)
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
endif()
enable_testing()
add_subdirectory(tests)
endif()
......@@ -8,6 +8,15 @@ It is entirely written in pure-C++11 and provides a clear and pleasant API
Full documentation is located at [http://pistache.io](http://pistache.io).
# Contributing
Pistache is an open-source project and will always stay open-source. However, working on an open-source project while having a full-time job is sometimes a difficult task to accomplish.
That's why your help is needed. If you would like to contribute to the project in any way (submitting ideas, fixing bugs, writing documentation, ...), please join the
[cpplang Slack channel](https://cpplang.now.sh/). Drop a private message to `@octal` and I will invite you to the channel dedicated to Pistache.
Hope to see you there !
# Example
## Hello World (server)
......@@ -15,7 +24,7 @@ Full documentation is located at [http://pistache.io](http://pistache.io).
```cpp
#include <pistache/endpoint.h>
using namespace Net;
using namespace Pistache;
struct HelloHandler : public Http::Handler {
HTTP_PROTOTYPE(HelloHandler)
......
......@@ -5,9 +5,9 @@
*/
#include "endpoint.h"
#include "pistache/endpoint.h"
using namespace Net;
using namespace Pistache;
class HelloHandler : public Http::Handler {
public:
......@@ -20,8 +20,8 @@ public:
};
int main() {
Net::Address addr(Net::Ipv4::any(), Net::Port(9080));
auto opts = Net::Http::Endpoint::options()
Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(9080));
auto opts = Pistache::Http::Endpoint::options()
.threads(1);
Http::Endpoint server(addr);
......
......@@ -31,6 +31,7 @@ namespace Http {
#define STATUS_CODES \
CODE(100, Continue, "Continue") \
CODE(101, Switching_Protocols, "Switching Protocols") \
CODE(102, Processing, "Processing") \
CODE(200, Ok, "OK") \
CODE(201, Created, "Created") \
CODE(202, Accepted, "Accepted") \
......@@ -38,6 +39,9 @@ namespace Http {
CODE(204, No_Content, "No Content") \
CODE(205, Reset_Content, "Reset Content") \
CODE(206, Partial_Content, "Partial Content") \
CODE(207, MultiStatus, "Multi-Status") \
CODE(208, Already_Reported, "Already Reported") \
CODE(226, IM_Used, "IM Used") \
CODE(300, Multiple_Choices, "Multiple Choices") \
CODE(301, Moved_Permanently, "Moved Permanently") \
CODE(302, Found, "Found") \
......@@ -45,6 +49,7 @@ namespace Http {
CODE(304, Not_Modified, "Not Modified") \
CODE(305, Use_Proxy, "Use Proxy") \
CODE(307, Temporary_Redirect, "Temporary Redirect") \
CODE(308, Permanent_Redirect, "Permanent Redirect") \
CODE(400, Bad_Request, "Bad Request") \
CODE(401, Unauthorized, "Unauthorized") \
CODE(402, Payment_Required, "Payment Required") \
......@@ -63,11 +68,30 @@ namespace Http {
CODE(415, Unsupported_Media_Type, "Unsupported Media Type") \
CODE(416, Requested_Range_Not_Satisfiable, "Requested Range Not Satisfiable") \
CODE(417, Expectation_Failed, "Expectation Failed") \
CODE(418, I_m_a_teapot, "I'm a teapot") \
CODE(421, Misdirected_Request, "Misdirected Request") \
CODE(422, Unprocessable_Entity, "Unprocessable Entity") \
CODE(423, Locked, "Locked") \
CODE(424, Failed_Dependency, "Failed Dependency") \
CODE(426, Upgrade_Required, "Upgrade Required") \
CODE(428, Precondition_Required, "Precondition Required") \
CODE(429, Too_Many_Requests, "Too Many Requests")\
CODE(431, Request_Header_Fields_Too_Large, "Request Header Fields Too Large") \
CODE(444, Connection_Closed_Without_Response, "Connection Closed Without Response") \
CODE(451, Unavailable_For_Legal_Reasons, "Unavailable For Legal Reasons") \
CODE(499, Client_Closed_Request, "Client Closed Request") \
CODE(500, Internal_Server_Error, "Internal Server Error") \
CODE(501, Not_Implemented, "Not Implemented") \
CODE(502, Bad_Gateway, "Bad Gateway") \
CODE(503, Service_Unavailable, "Service Unavailable") \
CODE(504, Gateway_Timeout, "Gateway Timeout")
CODE(504, Gateway_Timeout, "Gateway Timeout") \
CODE(505, HTTP_Version_Not_Supported, "HTTP Version Not Supported")\
CODE(506, Variant_Also_Negotiates, "Variant Also Negotiates") \
CODE(507, Insufficient_Storage, "Insufficient Storage") \
CODE(508, Loop_Detected, "Loop Detected") \
CODE(510, Not_Extended, "Not Extended") \
CODE(511, Network_Authentication_Required, "Network Authentication Required")\
CODE(599, Network_Connect_Timeout_Error, "Network Connect Timeout Error")
// 3.4. Character Sets
// See http://tools.ietf.org/html/rfc2978 and
......@@ -175,6 +199,7 @@ private:
};
const char* methodString(Method method);
const char* versionString(Version version);
const char* codeString(Code code);
std::ostream& operator<<(std::ostream& os, Version version);
......
......@@ -211,6 +211,10 @@ inline bool operator==(const MediaType& lhs, const MediaType& rhs) {
lhs.suffix() == rhs.suffix();
}
inline bool operator!=(const MediaType& lhs, const MediaType& rhs){
return !operator==(lhs, rhs);
}
} // namespace Mime
} // namespace Http
} // namespace Pistache
......
......@@ -28,35 +28,37 @@ set(bin_install_dir "bin/")
target_link_libraries(pistache pthread)
install(
TARGETS pistache
EXPORT ${targets_export_name}
ARCHIVE DESTINATION ${lib_install_dir}
LIBRARY DESTINATION ${lib_install_dir}
RUNTIME DESTINATION ${bin_install_dir}
INCLUDES DESTINATION ${include_install_dir})
install(
DIRECTORY "${PROJECT_SOURCE_DIR}/include/pistache"
DESTINATION ${include_install_dir}
FILES_MATCHING PATTERN "*.*h")
install(TARGETS pistache
EXPORT PistacheTargets
DESTINATION lib)
install(EXPORT PistacheTargets
if (PISTACHE_INSTALL)
install(
TARGETS pistache
EXPORT ${targets_export_name}
ARCHIVE DESTINATION ${lib_install_dir}
LIBRARY DESTINATION ${lib_install_dir}
RUNTIME DESTINATION ${bin_install_dir}
INCLUDES DESTINATION ${include_install_dir})
install(
DIRECTORY "${PROJECT_SOURCE_DIR}/include/pistache"
DESTINATION ${include_install_dir}
FILES_MATCHING PATTERN "*.*h")
install(TARGETS pistache
EXPORT PistacheTargets
DESTINATION lib)
install(EXPORT PistacheTargets
DESTINATION "lib/cmake/pistache"
EXPORT_LINK_INTERFACE_LIBRARIES
COMPONENT cmake-config
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"PistacheConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/PistacheConfig.cmake"
INSTALL_DESTINATION "lib/cmake/pistache"
)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/PistacheConfig.cmake"
DESTINATION "lib/cmake/pistache"
EXPORT_LINK_INTERFACE_LIBRARIES
COMPONENT cmake-config
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"PistacheConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/PistacheConfig.cmake"
INSTALL_DESTINATION "lib/cmake/pistache"
)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/PistacheConfig.cmake"
DESTINATION "lib/cmake/pistache"
COMPONENT cmake-config
)
)
endif()
......@@ -315,17 +315,14 @@ namespace Private {
headerRevert.ignore();
}
if (!cursor.advance(2)) return State::Again;
revert.ignore();
return State::Next;
}
State
BodyStep::apply(StreamCursor& cursor) {
if (message->body_.empty()) {
/* If this is the first time we are reading the body, skip the CRLF */
if (!cursor.advance(2)) return State::Again;
}
auto cl = message->headers_.tryGet<Header::ContentLength>();
auto te = message->headers_.tryGet<Header::TransferEncoding>();
......
......@@ -119,10 +119,10 @@ MediaType::parseRaw(const char* str, size_t len) {
//
// Watch out, this pattern is repeated throughout the function
do {
#define TYPE(val, s) \
if (match_raw(s, sizeof s - 1, cursor)) { \
top = Type::val; \
break; \
#define TYPE(val, s) \
if (match_string(s, sizeof s - 1, cursor, CaseSensitivity::Insensitive)) { \
top = Type::val; \
break; \
}
MIME_TYPES
#undef TYPE
......@@ -146,10 +146,10 @@ MediaType::parseRaw(const char* str, size_t len) {
sub = Subtype::Vendor;
} else {
do {
#define SUB_TYPE(val, s) \
if (match_raw(s, sizeof s - 1, cursor)) { \
sub = Subtype::val; \
break; \
#define SUB_TYPE(val, s) \
if (match_string(s, sizeof s - 1, cursor, CaseSensitivity::Insensitive)) { \
sub = Subtype::val; \
break; \
}
MIME_SUBTYPES
#undef SUB_TYPE
......@@ -176,10 +176,10 @@ MediaType::parseRaw(const char* str, size_t len) {
StreamCursor::Token suffixToken(cursor);
do {
#define SUFFIX(val, s, _) \
if (match_raw(s, sizeof s - 1, cursor)) { \
suffix = Suffix::val; \
break; \
#define SUFFIX(val, s, _) \
if (match_string(s, sizeof s - 1, cursor, CaseSensitivity::Insensitive)) { \
suffix = Suffix::val; \
break; \
}
MIME_SUFFIXES
#undef SUFFIX
......
......@@ -84,11 +84,13 @@ Address::Address(Ipv4 ip, Port port)
Address
Address::fromUnix(struct sockaddr* addr) {
struct sockaddr_in *in_addr = reinterpret_cast<struct sockaddr_in *>(addr);
std::string host = TRY_RET(inet_ntoa(in_addr->sin_addr));
char *host = inet_ntoa(in_addr->sin_addr);
assert(addr);
int port = ntohs(in_addr->sin_port);
return Address(std::move(host), port);
return Address(host, port);
}
std::string
......
......@@ -172,28 +172,27 @@ Route::match(const Http::Request& req) const
std::tuple<bool, std::vector<TypedParam>, std::vector<TypedParam>>
Route::match(const std::string& req) const
{
#define UNMATCH() std::make_tuple(false, std::vector<TypedParam>(), std::vector<TypedParam>())
static const auto NoMatch = std::make_tuple(false, std::vector<TypedParam>(), std::vector<TypedParam>());
auto reqFragments = Fragment::fromUrl(req);
if (reqFragments.size() > fragments_.size())
return UNMATCH();
return NoMatch;
std::vector<TypedParam> params;
std::vector<TypedParam> splats;
for (std::vector<Fragment>::size_type i = 0; i < fragments_.size(); ++i) {
const auto& fragment = fragments_[i];
if (i >= reqFragments.size()) {
if (fragment.isOptional())
continue;
return UNMATCH();
return NoMatch;
}
const auto& reqFragment = reqFragments[i];
if (!fragment.match(reqFragment))
return UNMATCH();
return NoMatch;
if (fragment.isParameter()) {
params.push_back(TypedParam(fragment.value(), reqFragment.value()));
......@@ -203,8 +202,6 @@ Route::match(const std::string& req) const
}
#undef UNMATCH
return make_tuple(true, std::move(params), std::move(splats));
}
......
......@@ -15,3 +15,4 @@ pistache_test(typeid_test)
pistache_test(router_test)
pistache_test(cookie_test)
pistache_test(view_test)
pistache_test(http_parsing_test)
#include "gtest/gtest.h"
#include <pistache/http.h>
using namespace Pistache;
// @Todo: Add an easy to use fixture to inject data for parsing tests.
TEST(http_parsing_test, should_parse_http_request_in_two_packets_issue_160)
{
Http::Private::Parser<Http::Request> parser;
auto feed = [&](const char* data)
{
parser.feed(data, std::strlen(data));
};
// First, we feed the parser with a Request-Line
feed("GET /hello HTTP/1.1\r\n");
ASSERT_EQ(parser.parse(), Http::Private::State::Again);
// @Todo @Completeness We should also assert that we are in the correct step. However, the step is currently not
// exposed by the parser. Since the parser is supposed to stay "private", we could either directly expose the step
// or return it from the parse() method.
// Let's now put some headers
feed("User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\r\n");
feed("Host: localhost\r\n");
feed("Content-Length: 5\r\n");
feed("\r\n");
ASSERT_EQ(parser.parse(), Http::Private::State::Again);
// Finally, we finish the body
feed("HELLO");
ASSERT_EQ(parser.parse(), Http::Private::State::Done);
}
\ No newline at end of file
......@@ -14,6 +14,7 @@ TEST(mime_test, basic_test) {
ASSERT_EQ(m1.toString(), "text/plain");
ASSERT_EQ(m1, MIME(Text, Plain));
ASSERT_NE(m1, MIME(Application, Json));
auto m2 = MIME3(Application, Xhtml, Xml);
ASSERT_EQ(m2.toString(), "application/xhtml+xml");
......@@ -137,3 +138,20 @@ TEST(mime_test, invalid_parsing) {
ASSERT_THROW(MediaType::fromString("text/html; q=0.21; charset="), HttpError);
ASSERT_THROW(MediaType::fromString("text/html; q=0.21; charset=ISO-8859-4; "), HttpError);
}
TEST(mime_test, should_parse_case_insensitive_issue_179)
{
parse("Application/Json", [](const Mime::MediaType& mime) {
ASSERT_EQ(mime, MIME(Application, Json));
ASSERT_TRUE(mime.q().isEmpty());
});
parse("aPpliCAtion/Xhtml+XML", [](const MediaType &mime) {
ASSERT_EQ(mime, MediaType(Type::Application, Subtype::Xhtml, Suffix::Xml));
ASSERT_TRUE(mime.q().isEmpty());
});
parse("Application/Xhtml+XML; q=0.78", [](const MediaType& mime) {
ASSERT_EQ(mime.q().getOrElse(Q(0)), Q(78));
});
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment