diff --git a/examples/server.js b/examples/server.js index 4182b1c3..3771f104 100644 --- a/examples/server.js +++ b/examples/server.js @@ -24,14 +24,16 @@ if (phantom.args.length !== 1) { console.log("request.headerValue(" + i + ") = " + request.headerValue(i)); } - var headers = "HTTP/1.1 200 OK\r\n" + - "Cache: no-cache\r\n" + - "Content-Type: text/html\r\n" + - "\r\n"; - response.writeHeaders(headers); - var contents = "YES!" + - "

pretty cool :)"; - response.writeBody(contents); + // we set the headers here + response.statusCode = 200; + response.headers = {"Cache": "no-cache", "Content-Type": "text/html"}; + // this is also possible: + response.setHeader("foo", "bar"); + // now we write the body + // note: the headers above will now be sent implictly + response.writeBody("YES!"); + // note: writeBody can be called multiple times + response.writeBody("

pretty cool :)"); }); var url = "http://localhost:" + port + "/foo/bar.php?asdf=true"; console.log(url); diff --git a/src/webserver.cpp b/src/webserver.cpp index d95dd48e..f593f234 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -183,19 +183,161 @@ QString WebServerRequest::headerValue(int header) const WebServerResponse::WebServerResponse(mg_connection *conn) : m_conn(conn) + , m_statusCode(200) + , m_headersSent(false) { } -void WebServerResponse::writeHeaders(const QString &headers) +const char* responseCodeString(int code) { - mg_printf(m_conn, "%s", qPrintable(headers)); + // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + switch (code) { + case 100: + return "Continue"; + case 101: + return "Switching Protocols"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 300: + return "Multiple Choices"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 307: + return "Temporary Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Request Entity Too Large"; + case 414: + return "Request-URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Requested Range not Satisfiable"; + case 417: + return "Expectation Failed"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 306: + // unused: fallthrough + default: + return ""; + } +} + +void WebServerResponse::writeHeaders(int statusCode, const QVariantMap &headers) +{ + ///TODO: what is the best-practice error handling in javascript? exceptions? + Q_ASSERT(!m_headersSent); + m_headersSent = true; + mg_printf(m_conn, "HTTP/1.1 %d %s\r\n", m_statusCode, responseCodeString(m_statusCode)); + QVariantMap::const_iterator it = headers.constBegin(); + while(it != headers.constEnd()) { + mg_printf(m_conn, "%s: %s\r\n", qPrintable(it.key()), qPrintable(it.value().toString())); + ++it; + } + mg_write(m_conn, "\r\n", 2); } void WebServerResponse::writeBody(const QString &body) { - mg_printf(m_conn, "%s", qPrintable(body)); + if (!m_headersSent) { + writeHeaders(m_statusCode, m_headers); + } + ///TODO: encoding?! + const QByteArray data = body.toLocal8Bit(); + mg_write(m_conn, data.constData(), data.size()); } +int WebServerResponse::statusCode() const +{ + return m_statusCode; +} + +void WebServerResponse::setStatusCode(int code) +{ + ///TODO: what is the best-practice error handling in javascript? exceptions? + Q_ASSERT(!m_headersSent); + m_statusCode = code; +} + +QString WebServerResponse::header(const QString &name) const +{ + return m_headers.value(name).toString(); +} + +void WebServerResponse::setHeader(const QString &name, const QString &value) +{ + ///TODO: what is the best-practice error handling in javascript? exceptions? + Q_ASSERT(!m_headersSent); + m_headers.insert(name, value); +} + +QVariantMap WebServerResponse::headers() const +{ + return m_headers; +} + +void WebServerResponse::setHeaders(const QVariantMap &headers) +{ + ///TODO: what is the best-practice error handling in javascript? exceptions? + Q_ASSERT(!m_headersSent); + m_headers = headers; +} //END WebServerResponse diff --git a/src/webserver.h b/src/webserver.h index 03e2011e..2a67ceae 100644 --- a/src/webserver.h +++ b/src/webserver.h @@ -32,6 +32,7 @@ #define WEBSERVER_H #include +#include ///TODO: is this ok, or should it be put into .cpp /// can be done by introducing a WebServerPrivate *d; @@ -110,17 +111,37 @@ class WebServerResponse : public QObject { Q_OBJECT + Q_PROPERTY(int statusCode READ statusCode WRITE setStatusCode); + Q_PROPERTY(QVariantMap headers READ headers WRITE setHeaders); public: WebServerResponse(mg_connection *conn); public slots: ///TODO: improve API - void writeHeaders(const QString& headers); - ///TODO: implictly write default headers if not called yet - void writeBody(const QString& body); + void writeHeaders(int statusCode, const QVariantMap &headers); + /// sends @p data to client and makes sure the headers are send beforehand + void writeBody(const QString &data); + + /// get the currently set status code, 200 is the default + int statusCode() const; + /// set the status code to @p code + void setStatusCode(int code); + + /// get the value of header @p name + QString header(const QString &name) const; + /// set the value of header @p name to @p value + void setHeader(const QString &name, const QString &value); + + /// get all headers + QVariantMap headers() const; + /// set all headers + void setHeaders(const QVariantMap &headers); private: mg_connection *m_conn; + int m_statusCode; + QVariantMap m_headers; + bool m_headersSent; }; #endif // WEBSERVER_H