/* * Copyright (C) 2012 by egnite GmbH * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * $Id$ */ #if !defined(HTTPD_EXCLUDE_DATE) #include #endif #include #include #include #include #include typedef struct _HTTP_RESPONSE_STATUS HTTP_RESPONSE_STATUS; struct _HTTP_RESPONSE_STATUS { int rs_code; const char *rs_text; }; #if HTTP_VERSION >= 0x10 static const HTTP_RESPONSE_STATUS response_list[] = { { 100, "Continue" }, { 101, "Switching Protocols" }, { 102, "Processing" }, { 103, "Checkpoint" }, { 200, "OK" }, { 201, "Created" }, { 202, "Accepted" }, #if HTTP_VERSION >= 0x11 { 203, "Non-Authoritative Information" }, #endif { 204, "No Content" }, { 205, "Reset Content" }, { 206, "Partial Content" }, { 207, "Multi-Status" }, { 208, "Already Reported" }, { 226, "IM Used" }, { 300, "Multiple Choices" }, { 301, "Moved Permanently" }, { 302, "Moved Temporarily" }, #if HTTP_VERSION >= 0x11 { 303, "See Other" }, #endif { 304, "Not Modified" }, #if HTTP_VERSION >= 0x11 { 305, "Use Proxy" }, #endif { 306, "Switch Proxy" }, #if HTTP_VERSION >= 0x11 { 307, "Temporary Redirect" }, #endif { 308, "Resume Incomplete" }, { 400, "Bad Request" }, { 401, "Unauthorized" }, { 402, "Payment Required" }, { 403, "Forbidden" }, { 404, "Not Found" }, { 405, "Not Allowed" }, { 406, "Not Acceptable" }, { 407, "Proxy Authentication Required" }, { 408, "Request Time-out" }, { 409, "Conflict" }, { 410, "Gone" }, { 411, "Length Required" }, { 412, "Precondition Failed" }, { 413, "Request Entity Too Large" }, { 414, "Request-URI Too Large" }, { 415, "Unsupported Media Type" }, { 416, "Requested Range Not Satisfiable" }, { 417, "Expectation Failed" }, { 418, "I'm a teapot" }, { 422, "Unprocessable Entity" }, { 423, "Locked" }, { 424, "Failed Dependency" }, { 425, "Unordered Collection" }, { 426, "Upgrade Required" }, { 428, "Precondition Required" }, { 429, "Too Many Requests" }, { 431, "Request Header Fields Too Large" }, { 444, "No Response" }, { 449, "Retry With" }, { 499, "Client Closed Request" }, { 500, "Internal Server Error" }, { 501, "Method Not Implemented" }, { 502, "Bad Gateway" }, { 503, "Service Temporarily Unavailable" }, { 504, "Gateway Time-out" }, { 505, "HTTP Version Not Supported" }, { 506, "Variant Also Negotiates" }, { 507, "Insufficient Storage" }, { 508, "Loop Detected" }, { 509, "Bandwidth Limit Exceeded" }, { 510, "Not Extended" }, { 511, "Network Authentication Required" }, { 598, "Network read timeout error" }, { 599, "Network connect timeout error" } }; #define HTTP_NUM_RESPONSES (sizeof(response_list) / sizeof(HTTP_RESPONSE_STATUS)) #endif const char *HttpResponseText(int code) { const char *rp = NULL; #if HTTP_VERSION >= 0x10 int i; for (i = 0; i < (int)HTTP_NUM_RESPONSES; i++) { if (code >= response_list[i].rs_code) { break; } } if (i < (int)HTTP_NUM_RESPONSES && code == response_list[i].rs_code) { rp = response_list[i].rs_text; } #endif if (rp == NULL) { static const char *err_txt = "Error"; static const char *ok_txt = "OK"; if (code < 400) { rp = ok_txt; } else { rp = err_txt; } } return rp; } void HttpSendStreamHeaderTop(HTTP_STREAM *stream, int status) { #if HTTP_VERSION >= 0x10 static const char fmt_P[] = "HTTP/%d.%d %d %s\r\nServer: uHTTP 0.0\r\n"; s_printf(stream, fmt_P, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, HttpResponseText(status)); #if !defined(HTTPD_EXCLUDE_DATE) { time_t now = time(NULL); s_vputs(stream, ct_Date, ": ", Rfc1123TimeString(gmtime(&now)), " GMT\r\n", NULL); } #endif #endif } void HttpSendHeaderTop(HTTPD_SESSION *hs, int status) { HttpSendStreamHeaderTop(hs->s_stream, status); } #if !defined(HTTPD_EXCLUDE_DATE) void HttpSendStreamHeaderDate(HTTP_STREAM *stream, time_t mtime) { #if HTTP_VERSION >= 0x10 if (mtime) { s_vputs(stream, ct_Last_Modified, ": ", Rfc1123TimeString(gmtime(&mtime)), " GMT\r\n", NULL); } #endif } void HttpSendHeaderDate(HTTPD_SESSION *hs, time_t mtime) { #if HTTP_VERSION >= 0x10 HttpSendStreamHeaderDate(hs->s_stream, mtime); #endif } #endif void HttpSendStreamHeaderBottom(HTTP_STREAM *stream, const char *type, const char *subtype, int conn, long bytes) { #if HTTP_VERSION >= 0x10 (void)conn; if (type && subtype) { s_vputs(stream, ct_Content_Type, ": ", type, "/", subtype, "\r\n", NULL); } if (bytes >= 0) { s_printf(stream, "%s: %ld\r\n", ct_Content_Length, bytes); } #if HTTP_VERSION >= 0x11 #if HTTP_KEEP_ALIVE_REQ if (conn != HTTP_CONN_KEEP_ALIVE) #endif { s_puts("Connection: close\r\n", stream); } #elif HTTP_KEEP_ALIVE_REQ if (conn == HTTP_CONN_KEEP_ALIVE) { s_puts("Connection: keep-alive\r\n", stream); } #endif s_puts("\r\n", stream); #endif } void HttpSendHeaderBottom(HTTPD_SESSION *hs, const char *type, const char *subtype, long bytes) { #if HTTP_VERSION >= 0x10 #if HTTP_KEEP_ALIVE_REQ if (bytes < 0) { hs->s_req.req_connection = HTTP_CONN_CLOSE; } #endif #if 0 #define GZIP_ID 0x8b1f if (first2bytes == GZIP_ID) { s_puts("Content-Encoding: gzip\r\n", hs->s_stream); } #endif HttpSendStreamHeaderBottom(hs->s_stream, type, subtype, hs->s_req.req_connection, bytes); #endif } void HttpSendStreamError(HTTP_STREAM *stream, int status, const char *realm) { static const char body[] = "%d %s%d %s\r\n"; const char *text = HttpResponseText(status); HttpSendStreamHeaderTop(stream, status); #if HTTP_VERSION >= 0x10 if (realm) { static const char auth_fmt_P[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n"; s_printf(stream, auth_fmt_P, realm); } HttpSendStreamHeaderBottom(stream, "text", "html", HTTP_CONN_CLOSE, sizeof(body) - 1 + 2 * (1 + strlen(text) - 2)); #endif s_printf(stream, body, status, text, status, text); s_flush(stream); } void HttpSendError(HTTPD_SESSION *hs, int status) { char *realm = NULL; #if HTTP_KEEP_ALIVE_REQ && (HTTP_VERSION >= 0x10) if (status >= 400) { //hs->s_req.req_connection = HTTP_CONN_CLOSE; } #endif #if HTTP_VERSION >= 0x10 if (status == 401) { realm = hs->s_req.req_realm; } #endif HttpSendStreamError(hs->s_stream, status, realm); } /*! * \brief Transmit a redirection page. */ int HttpSendRedirection(HTTPD_SESSION *hs, int code, ...) { va_list ap; int len; char *cp; char *loc; va_start(ap, code); for (len = 0; (cp = va_arg(ap, char *)) != NULL; len += strlen(cp)); va_end(ap); loc = malloc(len + 1); if (loc) { static const char body[] = "Continue\r\n"; HTTP_STREAM *sp = hs->s_stream; HttpSendHeaderTop(hs, code); va_start(ap, code); for (*loc = '\0'; (cp = va_arg(ap, char *)) != NULL; strcat(loc, cp)); va_end(ap); #if HTTP_VERSION >= 0x10 s_vputs(sp, ct_Location, ": ", loc, "\r\n", NULL); HttpSendHeaderBottom(hs, "text", "html", sizeof(body) - 1 + strlen(loc) - 2); #endif s_printf(sp, body, loc); s_flush(sp); free(loc); } return 0; }