1
\$\begingroup\$

This is my first "real" attempt at decoding an HTTP request on the server side.

This is the interface I think should be sufficient. However, because I am new to the details I would love to get some feedback on whether there is some missing information that a client would like to see.

#ifndef THORSANVIL_NISSE_PYNT_HTTP_H
#define THORSANVIL_NISSE_PYNT_HTTP_H

/*
 * An HTTP implementation of Pynt
 * Version 1:
 */

#include "PyntHTTPConfig.h"
#include "NisseServer/Pynt.h"
#include <string_view>

namespace ThorsAnvil::Nisse
{

enum class Version {HTTP1_0, HTTTP1_1, HTTP2, HTTP3, Uknown};
enum class Method  {GET, HEAD, OPTIONS, TRACE, PUT, DELETE, POST, PATCH, CONNECT, Other};

class URL
{
    public:
        std::string_view        href();     // 'http://user:pass@localhost:53/status?name=ryan#234'
        std::string_view        origin();   // 'http://localhost:53'
        std::string_view        protocol(); // 'http:'
        std::string_view        username(); // 'user'
        std::string_view        password(); // 'pass'
        std::string_view        host();     // 'localhost:53',
        std::string_view        hostname(); // 'localhost',
        std::string_view        port();     // '53'
        std::string_view        pathname(); // '/status',
        std::string_view        search();   // '?name=ryan',
        std::string_view        hash();     // '#234'

        std::string_view        param(std::string_view param);  // parm('name') => 'ryan'
};

class Header
{
    using CIterator = std::pair<std::string, std::vector<std::string>>;
    using const_iterator = CIterator;

    public:
        std::vector<std::string>&   getHeader(std::string_view header);
        bool                        hasHeader(std::string_view header);
        CIterator                   begin() const;
        CIterator                   end()   const;
};

class Request
{
    Version             httpVersion();
    Method              method();
    URL const&          url();
    std::string_view    httpRawRequest();   // GET /index.html HTTP/1.1

    Header const&       headers();
    Header const&       trailers();         // Trailers will return an empty Header() if body has not been read.
                                            // if (body().eof()) Then trailers have been read.

    std::istream&       body();             // Can be used to read the stream body.
                                            // It will auto eof() when no more data is available in the body.
                                            // Note this stream will auto decode the incoming message body based
                                            // on the 'content-encoding'
};

class Response
{
    void                setStatus(int code, std::string_view message);  // If not set default 200 OK
                                                                        // Can be modified anytime up to the headers being sent.
    Header&             headers();          // Headers will be locked after they are sent. By default this happens when data is sent to the body stream.
    Header&             trailers();
    void                done();             // Manually indicate complete. Done automatically by destructor.
                                            // But processes can use this to flush stream immediately if desired.

    std::ostream&       body();             // Format of body is determined by the 'content-encoding/content-length' header fields.
                                            // Note: Headers will be locked after you start writing to the body stream and thus can
                                            //       not be changed from default encoding.

    bool                isHeadersSent();
    bool                isDone();
};

class PyntHTTP: public Pynt
{
    public:
        virtual PyntResult handleRequest(std::iostream& stream) override;
        virtual PyntResult processRequest(Request const& request, Response& response) = 0;
};

}

#endif

Note: Based on extending the Pynt interface previously described.

Note: Though the interface uses std::istream and std::ostream on the server these are actually implemented by streams that understand how to yield control when reading/writing would block. Thus we get an intuitive interface that is simple to program against but is also efficient.

\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.