#ifndef NALL_HTTP_REQUEST_HPP #define NALL_HTTP_REQUEST_HPP #include namespace nall { namespace HTTP { struct Request : Message { using type = Request; enum class RequestType : unsigned { None, Head, Get, Post }; explicit operator bool() const { return requestType() != RequestType::None; } inline auto head(const function& callback) const -> bool; inline auto setHead() -> bool; inline auto body(const function& callback) const -> bool; inline auto setBody() -> bool; auto ipv4() const -> bool { return _ipv6 == false; } auto ipv6() const -> bool { return _ipv6 == true; } auto ip() const -> string { return _ip; } auto requestType() const -> RequestType { return _requestType; } auto setRequestType(RequestType value) -> void { _requestType = value; } auto path() const -> string { return _path; } auto setPath(const string& value) -> void { _path = value; } auto appendHeader(const string& name, const string& value = "") -> type& { return Message::appendHeader(name, value), *this; } auto removeHeader(const string& name) -> type& { return Message::removeHeader(name), *this; } auto setHeader(const string& name, const string& value = "") -> type& { return Message::setHeader(name, value), *this; } auto cookie(const string& name) const -> string { return _cookie.get(name); } auto setCookie(const string& name, const string& value = "") -> void { _cookie.set(name, value); } auto get(const string& name) const -> string { return _get.get(name); } auto setGet(const string& name, const string& value = "") -> void { _get.set(name, value); } auto post(const string& name) const -> string { return _post.get(name); } auto setPost(const string& name, const string& value = "") -> void { _post.set(name, value); } //private: bool _ipv6 = false; string _ip; RequestType _requestType = RequestType::None; string _path; Variables _cookie; Variables _get; Variables _post; }; auto Request::head(const function& callback) const -> bool { if(!callback) return false; string output; string request = path(); if(_get.size()) { request.append("?"); for(auto& get : _get) { request.append(get.name, "=", get.value, "&"); } request.rtrim("&", 1L); } switch(requestType()) { case RequestType::Head: output.append("HEAD ", request, " HTTP/1.1\r\n"); break; case RequestType::Get : output.append("GET ", request, " HTTP/1.1\r\n"); break; case RequestType::Post: output.append("POST ", request, " HTTP/1.1\r\n"); break; default: return false; } for(auto& header : _header) { output.append(header.name, ": ", header.value, "\r\n"); } output.append("\r\n"); return callback(output.binary(), output.size()); } auto Request::setHead() -> bool { lstring headers = _head.split("\n"); string request = headers.takeFirst().rtrim("\r"); string requestHost; if(request.iendsWith(" HTTP/1.0")) request.irtrim(" HTTP/1.0", 1L); else if(request.iendsWith(" HTTP/1.1")) request.irtrim(" HTTP/1.1", 1L); else return false; if(request.ibeginsWith("HEAD ")) request.iltrim("HEAD ", 1L), setRequestType(RequestType::Head); else if(request.ibeginsWith("GET " )) request.iltrim("GET ", 1L), setRequestType(RequestType::Get ); else if(request.ibeginsWith("POST ")) request.iltrim("POST ", 1L), setRequestType(RequestType::Post); else return false; //decode absolute URIs request.strip().iltrim("http://", 1L); if(!request.beginsWith("/")) { lstring components = request.split("/", 1L); requestHost = components(0); request = {"/", components(1)}; } lstring components = request.split("?", 1L); setPath(components(0)); if(auto queryString = components(1)) { for(auto& block : queryString.split("&")) { lstring variable = block.split("=", 1L); if(variable(0)) setGet(variable(0), variable(1)); } } for(auto& header : headers) { if(header.beginsWith(" ") || header.beginsWith("\t")) continue; auto part = header.split(":", 1L).strip(); if(!part[0] || part.size() != 2) continue; appendHeader(part[0], part[1]); if(part[0].iequals("Cookie")) { for(auto& block : part[1].split(";")) { lstring variable = block.split("=", 1L).strip(); variable(1).trim("\"", "\""); if(variable(0)) setCookie(variable(0), variable(1)); } } } if(requestHost) setHeader("Host", requestHost); //request URI overrides host header return true; } auto Request::body(const function& callback) const -> bool { if(!callback) return false; if(_body) { return callback(_body.binary(), _body.size()); } return true; } auto Request::setBody() -> bool { if(requestType() == RequestType::Post) { if(header("Content-Type").iequals("application/x-www-form-urlencoded")) { for(auto& block : _body.split("&")) { lstring variable = block.rtrim("\r").split("=", 1L); if(variable(0)) setPost(variable(0), variable(1)); } } } return true; } }} #endif