| /* |
| This file is part of libmicrohttpd |
| Copyright (C) 2007, 2009, 2010 Daniel Pittman and Christian Grothoff |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /** |
| * @file response.c |
| * @brief Methods for managing response objects |
| * @author Daniel Pittman |
| * @author Christian Grothoff |
| */ |
| |
| #include "internal.h" |
| #include "response.h" |
| |
| #if defined(_WIN32) && defined(MHD_W32_MUTEX_) |
| #ifndef WIN32_LEAN_AND_MEAN |
| #define WIN32_LEAN_AND_MEAN 1 |
| #endif /* !WIN32_LEAN_AND_MEAN */ |
| #include <windows.h> |
| #endif /* _WIN32 && MHD_W32_MUTEX_ */ |
| #if defined(_WIN32) |
| #include <io.h> /* for lseek(), read() */ |
| #endif /* _WIN32 */ |
| |
| |
| /** |
| * Add a header or footer line to the response. |
| * |
| * @param response response to add a header to |
| * @param kind header or footer |
| * @param header the header to add |
| * @param content value to add |
| * @return #MHD_NO on error (i.e. invalid header or content format). |
| */ |
| static int |
| add_response_entry (struct MHD_Response *response, |
| enum MHD_ValueKind kind, |
| const char *header, |
| const char *content) |
| { |
| struct MHD_HTTP_Header *hdr; |
| |
| if ( (NULL == response) || |
| (NULL == header) || |
| (NULL == content) || |
| (0 == strlen (header)) || |
| (0 == strlen (content)) || |
| (NULL != strchr (header, '\t')) || |
| (NULL != strchr (header, '\r')) || |
| (NULL != strchr (header, '\n')) || |
| (NULL != strchr (content, '\t')) || |
| (NULL != strchr (content, '\r')) || |
| (NULL != strchr (content, '\n')) ) |
| return MHD_NO; |
| if (NULL == (hdr = malloc (sizeof (struct MHD_HTTP_Header)))) |
| return MHD_NO; |
| if (NULL == (hdr->header = strdup (header))) |
| { |
| free (hdr); |
| return MHD_NO; |
| } |
| if (NULL == (hdr->value = strdup (content))) |
| { |
| free (hdr->header); |
| free (hdr); |
| return MHD_NO; |
| } |
| hdr->kind = kind; |
| hdr->next = response->first_header; |
| response->first_header = hdr; |
| return MHD_YES; |
| } |
| |
| |
| /** |
| * Add a header line to the response. |
| * |
| * @param response response to add a header to |
| * @param header the header to add |
| * @param content value to add |
| * @return #MHD_NO on error (i.e. invalid header or content format). |
| * @ingroup response |
| */ |
| int |
| MHD_add_response_header (struct MHD_Response *response, |
| const char *header, const char *content) |
| { |
| return add_response_entry (response, |
| MHD_HEADER_KIND, |
| header, |
| content); |
| } |
| |
| |
| /** |
| * Add a footer line to the response. |
| * |
| * @param response response to remove a header from |
| * @param footer the footer to delete |
| * @param content value to delete |
| * @return #MHD_NO on error (i.e. invalid footer or content format). |
| * @ingroup response |
| */ |
| int |
| MHD_add_response_footer (struct MHD_Response *response, |
| const char *footer, const char *content) |
| { |
| return add_response_entry (response, |
| MHD_FOOTER_KIND, |
| footer, |
| content); |
| } |
| |
| |
| /** |
| * Delete a header (or footer) line from the response. |
| * |
| * @param response response to remove a header from |
| * @param header the header to delete |
| * @param content value to delete |
| * @return #MHD_NO on error (no such header known) |
| * @ingroup response |
| */ |
| int |
| MHD_del_response_header (struct MHD_Response *response, |
| const char *header, |
| const char *content) |
| { |
| struct MHD_HTTP_Header *pos; |
| struct MHD_HTTP_Header *prev; |
| |
| if ( (NULL == header) || (NULL == content) ) |
| return MHD_NO; |
| prev = NULL; |
| pos = response->first_header; |
| while (pos != NULL) |
| { |
| if ((0 == strcmp (header, pos->header)) && |
| (0 == strcmp (content, pos->value))) |
| { |
| free (pos->header); |
| free (pos->value); |
| if (NULL == prev) |
| response->first_header = pos->next; |
| else |
| prev->next = pos->next; |
| free (pos); |
| return MHD_YES; |
| } |
| prev = pos; |
| pos = pos->next; |
| } |
| return MHD_NO; |
| } |
| |
| |
| /** |
| * Get all of the headers (and footers) added to a response. |
| * |
| * @param response response to query |
| * @param iterator callback to call on each header; |
| * maybe NULL (then just count headers) |
| * @param iterator_cls extra argument to @a iterator |
| * @return number of entries iterated over |
| * @ingroup response |
| */ |
| int |
| MHD_get_response_headers (struct MHD_Response *response, |
| MHD_KeyValueIterator iterator, void *iterator_cls) |
| { |
| struct MHD_HTTP_Header *pos; |
| int numHeaders = 0; |
| |
| for (pos = response->first_header; NULL != pos; pos = pos->next) |
| { |
| numHeaders++; |
| if ((NULL != iterator) && |
| (MHD_YES != iterator (iterator_cls, |
| pos->kind, pos->header, pos->value))) |
| break; |
| } |
| return numHeaders; |
| } |
| |
| |
| /** |
| * Get a particular header (or footer) from the response. |
| * |
| * @param response response to query |
| * @param key which header to get |
| * @return NULL if header does not exist |
| * @ingroup response |
| */ |
| const char * |
| MHD_get_response_header (struct MHD_Response *response, |
| const char *key) |
| { |
| struct MHD_HTTP_Header *pos; |
| |
| if (NULL == key) |
| return NULL; |
| for (pos = response->first_header; NULL != pos; pos = pos->next) |
| if (0 == strcmp (key, pos->header)) |
| return pos->value; |
| return NULL; |
| } |
| |
| |
| /** |
| * Create a response object. The response object can be extended with |
| * header information and then be used any number of times. |
| * |
| * @param size size of the data portion of the response, #MHD_SIZE_UNKNOWN for unknown |
| * @param block_size preferred block size for querying crc (advisory only, |
| * MHD may still call @a crc using smaller chunks); this |
| * is essentially the buffer size used for IO, clients |
| * should pick a value that is appropriate for IO and |
| * memory performance requirements |
| * @param crc callback to use to obtain response data |
| * @param crc_cls extra argument to @a crc |
| * @param crfc callback to call to free @a crc_cls resources |
| * @return NULL on error (i.e. invalid arguments, out of memory) |
| * @ingroup response |
| */ |
| struct MHD_Response * |
| MHD_create_response_from_callback (uint64_t size, |
| size_t block_size, |
| MHD_ContentReaderCallback crc, |
| void *crc_cls, |
| MHD_ContentReaderFreeCallback crfc) |
| { |
| struct MHD_Response *response; |
| |
| if ((NULL == crc) || (0 == block_size)) |
| return NULL; |
| if (NULL == (response = malloc (sizeof (struct MHD_Response) + block_size))) |
| return NULL; |
| memset (response, 0, sizeof (struct MHD_Response)); |
| response->fd = -1; |
| response->data = (void *) &response[1]; |
| response->data_buffer_size = block_size; |
| if (MHD_YES != MHD_mutex_create_ (&response->mutex)) |
| { |
| free (response); |
| return NULL; |
| } |
| response->crc = crc; |
| response->crfc = crfc; |
| response->crc_cls = crc_cls; |
| response->reference_count = 1; |
| response->total_size = size; |
| return response; |
| } |
| |
| |
| /** |
| * Set special flags and options for a response. |
| * |
| * @param response the response to modify |
| * @param flags to set for the response |
| * @param ... #MHD_RO_END terminated list of options |
| * @return #MHD_YES on success, #MHD_NO on error |
| */ |
| int |
| MHD_set_response_options (struct MHD_Response *response, |
| enum MHD_ResponseFlags flags, |
| ...) |
| { |
| va_list ap; |
| int ret; |
| enum MHD_ResponseOptions ro; |
| |
| ret = MHD_YES; |
| response->flags = flags; |
| va_start (ap, flags); |
| while (MHD_RO_END != (ro = va_arg (ap, enum MHD_ResponseOptions))) |
| { |
| switch (ro) |
| { |
| default: |
| ret = MHD_NO; |
| break; |
| } |
| } |
| va_end (ap); |
| return ret; |
| } |
| |
| |
| /** |
| * Given a file descriptor, read data from the file |
| * to generate the response. |
| * |
| * @param cls pointer to the response |
| * @param pos offset in the file to access |
| * @param buf where to write the data |
| * @param max number of bytes to write at most |
| * @return number of bytes written |
| */ |
| static ssize_t |
| file_reader (void *cls, uint64_t pos, char *buf, size_t max) |
| { |
| struct MHD_Response *response = cls; |
| ssize_t n; |
| |
| (void) lseek (response->fd, pos + response->fd_off, SEEK_SET); |
| n = read (response->fd, buf, max); |
| if (0 == n) |
| return MHD_CONTENT_READER_END_OF_STREAM; |
| if (n < 0) |
| return MHD_CONTENT_READER_END_WITH_ERROR; |
| return n; |
| } |
| |
| |
| /** |
| * Destroy file reader context. Closes the file |
| * descriptor. |
| * |
| * @param cls pointer to file descriptor |
| */ |
| static void |
| free_callback (void *cls) |
| { |
| struct MHD_Response *response = cls; |
| |
| (void) close (response->fd); |
| response->fd = -1; |
| } |
| |
| |
| /** |
| * Create a response object. The response object can be extended with |
| * header information and then be used any number of times. |
| * |
| * @param size size of the data portion of the response |
| * @param fd file descriptor referring to a file on disk with the |
| * data; will be closed when response is destroyed; |
| * fd should be in 'blocking' mode |
| * @param offset offset to start reading from in the file; |
| * Be careful! `off_t` may have been compiled to be a |
| * 64-bit variable for MHD, in which case your application |
| * also has to be compiled using the same options! Read |
| * the MHD manual for more details. |
| * @return NULL on error (i.e. invalid arguments, out of memory) |
| * @ingroup response |
| */ |
| struct MHD_Response * |
| MHD_create_response_from_fd_at_offset (size_t size, |
| int fd, |
| off_t offset) |
| { |
| struct MHD_Response *response; |
| |
| response = MHD_create_response_from_callback (size, |
| 4 * 1024, |
| &file_reader, |
| NULL, |
| &free_callback); |
| if (NULL == response) |
| return NULL; |
| response->fd = fd; |
| response->fd_off = offset; |
| response->crc_cls = response; |
| return response; |
| } |
| |
| |
| /** |
| * Create a response object. The response object can be extended with |
| * header information and then be used any number of times. |
| * |
| * @param size size of the data portion of the response |
| * @param fd file descriptor referring to a file on disk with the data |
| * @return NULL on error (i.e. invalid arguments, out of memory) |
| * @ingroup response |
| */ |
| struct MHD_Response * |
| MHD_create_response_from_fd (size_t size, |
| int fd) |
| { |
| return MHD_create_response_from_fd_at_offset (size, fd, 0); |
| } |
| |
| |
| /** |
| * Create a response object. The response object can be extended with |
| * header information and then be used any number of times. |
| * |
| * @param size size of the @a data portion of the response |
| * @param data the data itself |
| * @param must_free libmicrohttpd should free data when done |
| * @param must_copy libmicrohttpd must make a copy of @a data |
| * right away, the data maybe released anytime after |
| * this call returns |
| * @return NULL on error (i.e. invalid arguments, out of memory) |
| * @deprecated use #MHD_create_response_from_buffer instead |
| * @ingroup response |
| */ |
| struct MHD_Response * |
| MHD_create_response_from_data (size_t size, |
| void *data, int must_free, int must_copy) |
| { |
| struct MHD_Response *response; |
| void *tmp; |
| |
| if ((NULL == data) && (size > 0)) |
| return NULL; |
| if (NULL == (response = malloc (sizeof (struct MHD_Response)))) |
| return NULL; |
| memset (response, 0, sizeof (struct MHD_Response)); |
| response->fd = -1; |
| if (MHD_YES != MHD_mutex_create_ (&response->mutex)) |
| { |
| free (response); |
| return NULL; |
| } |
| if ((must_copy) && (size > 0)) |
| { |
| if (NULL == (tmp = malloc (size))) |
| { |
| (void) MHD_mutex_destroy_ (&response->mutex); |
| free (response); |
| return NULL; |
| } |
| memcpy (tmp, data, size); |
| must_free = MHD_YES; |
| data = tmp; |
| } |
| response->crc = NULL; |
| response->crfc = must_free ? &free : NULL; |
| response->crc_cls = must_free ? data : NULL; |
| response->reference_count = 1; |
| response->total_size = size; |
| response->data = data; |
| response->data_size = size; |
| return response; |
| } |
| |
| |
| /** |
| * Create a response object. The response object can be extended with |
| * header information and then be used any number of times. |
| * |
| * @param size size of the data portion of the response |
| * @param buffer size bytes containing the response's data portion |
| * @param mode flags for buffer management |
| * @return NULL on error (i.e. invalid arguments, out of memory) |
| * @ingroup response |
| */ |
| struct MHD_Response * |
| MHD_create_response_from_buffer (size_t size, |
| void *buffer, |
| enum MHD_ResponseMemoryMode mode) |
| { |
| return MHD_create_response_from_data (size, |
| buffer, |
| mode == MHD_RESPMEM_MUST_FREE, |
| mode == MHD_RESPMEM_MUST_COPY); |
| } |
| |
| |
| /** |
| * Destroy a response object and associated resources. Note that |
| * libmicrohttpd may keep some of the resources around if the response |
| * is still in the queue for some clients, so the memory may not |
| * necessarily be freed immediatley. |
| * |
| * @param response response to destroy |
| * @ingroup response |
| */ |
| void |
| MHD_destroy_response (struct MHD_Response *response) |
| { |
| struct MHD_HTTP_Header *pos; |
| |
| if (NULL == response) |
| return; |
| (void) MHD_mutex_lock_ (&response->mutex); |
| if (0 != --(response->reference_count)) |
| { |
| (void) MHD_mutex_unlock_ (&response->mutex); |
| return; |
| } |
| (void) MHD_mutex_unlock_ (&response->mutex); |
| (void) MHD_mutex_destroy_ (&response->mutex); |
| if (response->crfc != NULL) |
| response->crfc (response->crc_cls); |
| while (NULL != response->first_header) |
| { |
| pos = response->first_header; |
| response->first_header = pos->next; |
| free (pos->header); |
| free (pos->value); |
| free (pos); |
| } |
| free (response); |
| } |
| |
| |
| void |
| MHD_increment_response_rc (struct MHD_Response *response) |
| { |
| (void) MHD_mutex_lock_ (&response->mutex); |
| (response->reference_count)++; |
| (void) MHD_mutex_unlock_ (&response->mutex); |
| } |
| |
| |
| /* end of response.c */ |