diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-02-17 09:43:09 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-02-17 09:43:09 +0100 |
commit | 25385c3c76cd81613761031f6f87c4087c2975eb (patch) | |
tree | 02493d62a8f96cec60347a40fb5341bb5d041caa | |
parent | ed7d85d6a835bc3528cb66a324dac93af1d3e8bc (diff) | |
download | libmicrohttpd-25385c3c76cd81613761031f6f87c4087c2975eb.tar.gz libmicrohttpd-25385c3c76cd81613761031f6f87c4087c2975eb.zip |
more work on connection_call_handlers.c
-rw-r--r-- | src/gnutls/idle_ready.c | 12 | ||||
-rw-r--r-- | src/include/microhttpd2.h | 66 | ||||
-rw-r--r-- | src/lib/action_continue.c | 2 | ||||
-rw-r--r-- | src/lib/action_from_response.c | 2 | ||||
-rw-r--r-- | src/lib/action_parse_post.c | 2 | ||||
-rw-r--r-- | src/lib/action_process_upload.c | 2 | ||||
-rw-r--r-- | src/lib/action_suspend.c | 4 | ||||
-rw-r--r-- | src/lib/connection_call_handlers.c | 1708 | ||||
-rw-r--r-- | src/lib/connection_close.c | 4 | ||||
-rw-r--r-- | src/lib/internal.c | 48 | ||||
-rw-r--r-- | src/lib/internal.h | 27 |
11 files changed, 1704 insertions, 173 deletions
diff --git a/src/gnutls/idle_ready.c b/src/gnutls/idle_ready.c new file mode 100644 index 00000000..0e315950 --- /dev/null +++ b/src/gnutls/idle_ready.c | |||
@@ -0,0 +1,12 @@ | |||
1 | enum MHD_Bool | ||
2 | (*idle_ready)(void *cls, | ||
3 | struct MHD_TLS_ConnectionState *cs); | ||
4 | |||
5 | |||
6 | if (MHD_TLS_CONN_NO_TLS != connection->tls_state) | ||
7 | { /* HTTPS connection. */ | ||
8 | if ((MHD_TLS_CONN_INIT <= connection->tls_state) && | ||
9 | (MHD_TLS_CONN_CONNECTED > connection->tls_state)) | ||
10 | return false; | ||
11 | } | ||
12 | return true; | ||
diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h index f54d16b2..05fa58e9 100644 --- a/src/include/microhttpd2.h +++ b/src/include/microhttpd2.h | |||
@@ -493,6 +493,27 @@ enum MHD_StatusCode | |||
493 | */ | 493 | */ |
494 | MHD_SC_CONNECTION_WRITE_FAIL_CLOSED = 40003, | 494 | MHD_SC_CONNECTION_WRITE_FAIL_CLOSED = 40003, |
495 | 495 | ||
496 | /** | ||
497 | * MHD is returning an error because the header provided | ||
498 | * by the client is too big. | ||
499 | */ | ||
500 | MHD_SC_CLIENT_HEADER_TOO_BIG = 40004, | ||
501 | |||
502 | /** | ||
503 | * An HTTP/1.1 request was sent without the "Host:" header. | ||
504 | */ | ||
505 | MHD_SC_HOST_HEADER_MISSING = 40005, | ||
506 | |||
507 | /** | ||
508 | * The given content length was not a number. | ||
509 | */ | ||
510 | MHD_SC_CONTENT_LENGTH_MALFORMED = 40006, | ||
511 | |||
512 | /** | ||
513 | * The given uploaded, chunked-encoded body was malformed. | ||
514 | */ | ||
515 | MHD_SC_CHUNKED_ENCODING_MALFORMED = 40007, | ||
516 | |||
496 | 517 | ||
497 | 518 | ||
498 | /* 50000-level errors are because of an error internal | 519 | /* 50000-level errors are because of an error internal |
@@ -782,6 +803,18 @@ enum MHD_StatusCode | |||
782 | */ | 803 | */ |
783 | MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED = 50054, | 804 | MHD_SC_STATEMACHINE_FAILURE_CONNECTION_CLOSED = 50054, |
784 | 805 | ||
806 | /** | ||
807 | * Failed to allocate memory in connection's pool | ||
808 | * to parse the cookie header. | ||
809 | */ | ||
810 | MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE = 50055, | ||
811 | |||
812 | /** | ||
813 | * MHD failed to build the response header. | ||
814 | */ | ||
815 | MHD_SC_FAILED_RESPONSE_HEADER_GENERATION = 50056, | ||
816 | |||
817 | |||
785 | 818 | ||
786 | /* 60000-level errors are because the application | 819 | /* 60000-level errors are because the application |
787 | logic did something wrong or generated an error. */ | 820 | logic did something wrong or generated an error. */ |
@@ -820,6 +853,19 @@ enum MHD_StatusCode | |||
820 | */ | 853 | */ |
821 | MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60005, | 854 | MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED = 60005, |
822 | 855 | ||
856 | /** | ||
857 | * MHD is closing a connection because the application | ||
858 | * callback told it to do so. | ||
859 | */ | ||
860 | MHD_SC_APPLICATION_CALLBACK_FAILURE_CLOSED = 60006, | ||
861 | |||
862 | /** | ||
863 | * Application only partially processed upload and did | ||
864 | * not suspend connection. This may result in a hung | ||
865 | * connection. | ||
866 | */ | ||
867 | MHD_SC_APPLICATION_HUNG_CONNECTION = 60007, | ||
868 | |||
823 | 869 | ||
824 | }; | 870 | }; |
825 | 871 | ||
@@ -1409,7 +1455,7 @@ enum MHD_Method | |||
1409 | * if the socket must be closed due to a serios | 1455 | * if the socket must be closed due to a serios |
1410 | * error while handling the request | 1456 | * error while handling the request |
1411 | */ | 1457 | */ |
1412 | typedef struct MHD_Action * | 1458 | typedef const struct MHD_Action * |
1413 | (*MHD_RequestCallback) (void *cls, | 1459 | (*MHD_RequestCallback) (void *cls, |
1414 | struct MHD_Request *request, | 1460 | struct MHD_Request *request, |
1415 | const char *url, | 1461 | const char *url, |
@@ -2791,7 +2837,7 @@ MHD_get_reason_phrase_for (enum MHD_HTTP_StatusCode code); | |||
2791 | * | 2837 | * |
2792 | * @return action to cause a request to be suspended. | 2838 | * @return action to cause a request to be suspended. |
2793 | */ | 2839 | */ |
2794 | _MHD_EXTERN struct MHD_Action * | 2840 | _MHD_EXTERN const struct MHD_Action * |
2795 | MHD_action_suspend (void); | 2841 | MHD_action_suspend (void); |
2796 | 2842 | ||
2797 | 2843 | ||
@@ -2826,10 +2872,10 @@ struct MHD_Response; | |||
2826 | 2872 | ||
2827 | 2873 | ||
2828 | /** | 2874 | /** |
2829 | * Converts a @a response to an action. If @a consume | 2875 | * Converts a @a response to an action. If @a destroy_after_use |
2830 | * is set, the reference to the @a response is consumed | 2876 | * is set, the reference to the @a response is consumed |
2831 | * by the conversion. If @a consume is #MHD_NO, then | 2877 | * by the conversion. If @a consume is #MHD_NO, then |
2832 | * the response can be converted to actions in the future. | 2878 | * the @a response can be converted to actions in the future. |
2833 | * However, the @a response is frozen by this step and | 2879 | * However, the @a response is frozen by this step and |
2834 | * must no longer be modified (i.e. by setting headers). | 2880 | * must no longer be modified (i.e. by setting headers). |
2835 | * | 2881 | * |
@@ -2842,7 +2888,7 @@ struct MHD_Response; | |||
2842 | * as a response *is* an action. As no memory is | 2888 | * as a response *is* an action. As no memory is |
2843 | * allocated, this operation cannot fail. | 2889 | * allocated, this operation cannot fail. |
2844 | */ | 2890 | */ |
2845 | _MHD_EXTERN struct MHD_Action * | 2891 | _MHD_EXTERN const struct MHD_Action * |
2846 | MHD_action_from_response (struct MHD_Response *response, | 2892 | MHD_action_from_response (struct MHD_Response *response, |
2847 | enum MHD_Bool destroy_after_use) | 2893 | enum MHD_Bool destroy_after_use) |
2848 | MHD_NONNULL(1); | 2894 | MHD_NONNULL(1); |
@@ -3364,7 +3410,7 @@ MHD_response_get_header (struct MHD_Response *response, | |||
3364 | * | 3410 | * |
3365 | * @return action operation, never NULL | 3411 | * @return action operation, never NULL |
3366 | */ | 3412 | */ |
3367 | _MHD_EXTERN struct MHD_Action * | 3413 | _MHD_EXTERN const struct MHD_Action * |
3368 | MHD_action_continue (void); | 3414 | MHD_action_continue (void); |
3369 | 3415 | ||
3370 | 3416 | ||
@@ -3386,7 +3432,7 @@ MHD_action_continue (void); | |||
3386 | * NULL to close the socket, or a response | 3432 | * NULL to close the socket, or a response |
3387 | * to discard the rest of the upload and return the data given | 3433 | * to discard the rest of the upload and return the data given |
3388 | */ | 3434 | */ |
3389 | typedef struct MHD_Action * | 3435 | typedef const struct MHD_Action * |
3390 | (*MHD_UploadCallback) (void *cls, | 3436 | (*MHD_UploadCallback) (void *cls, |
3391 | const char *upload_data, | 3437 | const char *upload_data, |
3392 | size_t *upload_data_size); | 3438 | size_t *upload_data_size); |
@@ -3400,7 +3446,7 @@ typedef struct MHD_Action * | |||
3400 | * @return NULL on error (out of memory) | 3446 | * @return NULL on error (out of memory) |
3401 | * @ingroup action | 3447 | * @ingroup action |
3402 | */ | 3448 | */ |
3403 | _MHD_EXTERN struct MHD_Action * | 3449 | _MHD_EXTERN const struct MHD_Action * |
3404 | MHD_action_process_upload (MHD_UploadCallback uc, | 3450 | MHD_action_process_upload (MHD_UploadCallback uc, |
3405 | void *uc_cls) | 3451 | void *uc_cls) |
3406 | MHD_NONNULL(1); | 3452 | MHD_NONNULL(1); |
@@ -3429,7 +3475,7 @@ MHD_action_process_upload (MHD_UploadCallback uc, | |||
3429 | * NULL to close the socket, or a response | 3475 | * NULL to close the socket, or a response |
3430 | * to discard the rest of the upload and return the data given | 3476 | * to discard the rest of the upload and return the data given |
3431 | */ | 3477 | */ |
3432 | typedef struct MHD_Action * | 3478 | typedef const struct MHD_Action * |
3433 | (*MHD_PostDataIterator) (void *cls, | 3479 | (*MHD_PostDataIterator) (void *cls, |
3434 | enum MHD_ValueKind kind, | 3480 | enum MHD_ValueKind kind, |
3435 | const char *key, | 3481 | const char *key, |
@@ -3465,7 +3511,7 @@ typedef struct MHD_Action * | |||
3465 | * otherwise a PP handle | 3511 | * otherwise a PP handle |
3466 | * @ingroup request | 3512 | * @ingroup request |
3467 | */ | 3513 | */ |
3468 | _MHD_EXTERN struct MHD_Action * | 3514 | _MHD_EXTERN const struct MHD_Action * |
3469 | MHD_action_parse_post (size_t buffer_size, | 3515 | MHD_action_parse_post (size_t buffer_size, |
3470 | MHD_PostDataIterator iter, | 3516 | MHD_PostDataIterator iter, |
3471 | void *iter_cls) | 3517 | void *iter_cls) |
diff --git a/src/lib/action_continue.c b/src/lib/action_continue.c index 40cb4a12..20c351ad 100644 --- a/src/lib/action_continue.c +++ b/src/lib/action_continue.c | |||
@@ -50,7 +50,7 @@ cont_action (void *cls, | |||
50 | * | 50 | * |
51 | * @return action operation, never NULL | 51 | * @return action operation, never NULL |
52 | */ | 52 | */ |
53 | struct MHD_Action * | 53 | const struct MHD_Action * |
54 | MHD_action_continue (void) | 54 | MHD_action_continue (void) |
55 | { | 55 | { |
56 | static struct MHD_Action acont = { | 56 | static struct MHD_Action acont = { |
diff --git a/src/lib/action_from_response.c b/src/lib/action_from_response.c index 9c303e0c..d3dccc5f 100644 --- a/src/lib/action_from_response.c +++ b/src/lib/action_from_response.c | |||
@@ -111,7 +111,7 @@ response_action (void *cls, | |||
111 | * as a response *is* an action. As no memory is | 111 | * as a response *is* an action. As no memory is |
112 | * allocated, this operation cannot fail. | 112 | * allocated, this operation cannot fail. |
113 | */ | 113 | */ |
114 | _MHD_EXTERN struct MHD_Action * | 114 | _MHD_EXTERN const struct MHD_Action * |
115 | MHD_action_from_response (struct MHD_Response *response, | 115 | MHD_action_from_response (struct MHD_Response *response, |
116 | enum MHD_Bool destroy_after_use) | 116 | enum MHD_Bool destroy_after_use) |
117 | { | 117 | { |
diff --git a/src/lib/action_parse_post.c b/src/lib/action_parse_post.c index 202e52f1..c60e793d 100644 --- a/src/lib/action_parse_post.c +++ b/src/lib/action_parse_post.c | |||
@@ -50,7 +50,7 @@ | |||
50 | * otherwise a PP handle | 50 | * otherwise a PP handle |
51 | * @ingroup request | 51 | * @ingroup request |
52 | */ | 52 | */ |
53 | struct MHD_Action * | 53 | const struct MHD_Action * |
54 | MHD_action_parse_post (size_t buffer_size, | 54 | MHD_action_parse_post (size_t buffer_size, |
55 | MHD_PostDataIterator iter, | 55 | MHD_PostDataIterator iter, |
56 | void *iter_cls) | 56 | void *iter_cls) |
diff --git a/src/lib/action_process_upload.c b/src/lib/action_process_upload.c index 33221486..cafd5d3c 100644 --- a/src/lib/action_process_upload.c +++ b/src/lib/action_process_upload.c | |||
@@ -73,7 +73,7 @@ upload_action (void *cls, | |||
73 | * @return NULL on error (out of memory) | 73 | * @return NULL on error (out of memory) |
74 | * @ingroup action | 74 | * @ingroup action |
75 | */ | 75 | */ |
76 | struct MHD_Action * | 76 | const struct MHD_Action * |
77 | MHD_action_process_upload (MHD_UploadCallback uc, | 77 | MHD_action_process_upload (MHD_UploadCallback uc, |
78 | void *uc_cls) | 78 | void *uc_cls) |
79 | { | 79 | { |
diff --git a/src/lib/action_suspend.c b/src/lib/action_suspend.c index 9e5e1301..da27f124 100644 --- a/src/lib/action_suspend.c +++ b/src/lib/action_suspend.c | |||
@@ -123,10 +123,10 @@ suspend_action (void *cls, | |||
123 | * | 123 | * |
124 | * @return action to cause a request to be suspended. | 124 | * @return action to cause a request to be suspended. |
125 | */ | 125 | */ |
126 | struct MHD_Action * | 126 | const struct MHD_Action * |
127 | MHD_action_suspend (void) | 127 | MHD_action_suspend (void) |
128 | { | 128 | { |
129 | static struct MHD_Action suspend = { | 129 | static const struct MHD_Action suspend = { |
130 | .action = &suspend_action, | 130 | .action = &suspend_action, |
131 | .action_cls = NULL | 131 | .action_cls = NULL |
132 | }; | 132 | }; |
diff --git a/src/lib/connection_call_handlers.c b/src/lib/connection_call_handlers.c index 346089a2..9e6276eb 100644 --- a/src/lib/connection_call_handlers.c +++ b/src/lib/connection_call_handlers.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "connection_update_last_activity.h" | 26 | #include "connection_update_last_activity.h" |
27 | #include "connection_close.h" | 27 | #include "connection_close.h" |
28 | 28 | ||
29 | |||
29 | #ifdef MHD_LINUX_SOLARIS_SENDFILE | 30 | #ifdef MHD_LINUX_SOLARIS_SENDFILE |
30 | #include <sys/sendfile.h> | 31 | #include <sys/sendfile.h> |
31 | #endif /* MHD_LINUX_SOLARIS_SENDFILE */ | 32 | #endif /* MHD_LINUX_SOLARIS_SENDFILE */ |
@@ -46,6 +47,59 @@ | |||
46 | */ | 47 | */ |
47 | #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) | 48 | #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000) |
48 | 49 | ||
50 | |||
51 | /** | ||
52 | * Response text used when the request (http header) is too big to | ||
53 | * be processed. | ||
54 | * | ||
55 | * Intentionally empty here to keep our memory footprint | ||
56 | * minimal. | ||
57 | */ | ||
58 | #ifdef HAVE_MESSAGES | ||
59 | #define REQUEST_TOO_BIG "<html><head><title>Request too big</title></head><body>Your HTTP header was too big for the memory constraints of this webserver.</body></html>" | ||
60 | #else | ||
61 | #define REQUEST_TOO_BIG "" | ||
62 | #endif | ||
63 | |||
64 | /** | ||
65 | * Response text used when the request (http header) does not | ||
66 | * contain a "Host:" header and still claims to be HTTP 1.1. | ||
67 | * | ||
68 | * Intentionally empty here to keep our memory footprint | ||
69 | * minimal. | ||
70 | */ | ||
71 | #ifdef HAVE_MESSAGES | ||
72 | #define REQUEST_LACKS_HOST "<html><head><title>"Host:" header required</title></head><body>In HTTP 1.1, requests must include a "Host:" header, and your HTTP 1.1 request lacked such a header.</body></html>" | ||
73 | #else | ||
74 | #define REQUEST_LACKS_HOST "" | ||
75 | #endif | ||
76 | |||
77 | /** | ||
78 | * Response text used when the request (http header) is | ||
79 | * malformed. | ||
80 | * | ||
81 | * Intentionally empty here to keep our memory footprint | ||
82 | * minimal. | ||
83 | */ | ||
84 | #ifdef HAVE_MESSAGES | ||
85 | #define REQUEST_MALFORMED "<html><head><title>Request malformed</title></head><body>Your HTTP request was syntactically incorrect.</body></html>" | ||
86 | #else | ||
87 | #define REQUEST_MALFORMED "" | ||
88 | #endif | ||
89 | |||
90 | /** | ||
91 | * Response text used when there is an internal server error. | ||
92 | * | ||
93 | * Intentionally empty here to keep our memory footprint | ||
94 | * minimal. | ||
95 | */ | ||
96 | #ifdef HAVE_MESSAGES | ||
97 | #define INTERNAL_ERROR "<html><head><title>Internal server error</title></head><body>Please ask the developer of this Web server to carefully read the GNU libmicrohttpd documentation about connection management and blocking.</body></html>" | ||
98 | #else | ||
99 | #define INTERNAL_ERROR "" | ||
100 | #endif | ||
101 | |||
102 | |||
49 | #ifdef HAVE_FREEBSD_SENDFILE | 103 | #ifdef HAVE_FREEBSD_SENDFILE |
50 | #ifdef SF_FLAGS | 104 | #ifdef SF_FLAGS |
51 | /** | 105 | /** |
@@ -931,7 +985,647 @@ MHD_request_handle_write_ (struct MHD_Request *request) | |||
931 | _("Internal error\n")); | 985 | _("Internal error\n")); |
932 | break; | 986 | break; |
933 | } | 987 | } |
934 | return; | 988 | } |
989 | |||
990 | |||
991 | /** | ||
992 | * Check whether request header contains particular token. | ||
993 | * | ||
994 | * Token could be surrounded by spaces and tabs and delimited by comma. | ||
995 | * Case-insensitive match used for header names and tokens. | ||
996 | * @param request the request to get values from | ||
997 | * @param header the header name | ||
998 | * @param token the token to find | ||
999 | * @param token_len the length of token, not including optional | ||
1000 | * terminating null-character. | ||
1001 | * @return true if token is found in specified header, | ||
1002 | * false otherwise | ||
1003 | */ | ||
1004 | static bool | ||
1005 | MHD_lookup_header_token_ci (const struct MHD_Request *request, | ||
1006 | const char *header, | ||
1007 | const char *token, | ||
1008 | size_t token_len) | ||
1009 | { | ||
1010 | struct MHD_HTTP_Header *pos; | ||
1011 | |||
1012 | if ( (NULL == request) || /* FIXME: require non-null? */ | ||
1013 | (NULL == header) || /* FIXME: require non-null? */ | ||
1014 | (0 == header[0]) || | ||
1015 | (NULL == token) || | ||
1016 | (0 == token[0]) ) | ||
1017 | return false; | ||
1018 | for (pos = request->headers_received; NULL != pos; pos = pos->next) | ||
1019 | { | ||
1020 | if ( (0 != (pos->kind & MHD_HEADER_KIND)) && | ||
1021 | ( (header == pos->header) || | ||
1022 | (MHD_str_equal_caseless_(header, | ||
1023 | pos->header)) ) && | ||
1024 | (MHD_str_has_token_caseless_ (pos->value, | ||
1025 | token, | ||
1026 | token_len)) ) | ||
1027 | return true; | ||
1028 | } | ||
1029 | return false; | ||
1030 | } | ||
1031 | |||
1032 | |||
1033 | /** | ||
1034 | * Check whether request header contains particular static @a tkn. | ||
1035 | * | ||
1036 | * Token could be surrounded by spaces and tabs and delimited by comma. | ||
1037 | * Case-insensitive match used for header names and tokens. | ||
1038 | * @param r the request to get values from | ||
1039 | * @param h the header name | ||
1040 | * @param tkn the static string of token to find | ||
1041 | * @return true if token is found in specified header, | ||
1042 | * false otherwise | ||
1043 | */ | ||
1044 | #define MHD_lookup_header_s_token_ci(r,h,tkn) \ | ||
1045 | MHD_lookup_header_token_ci((r),(h),(tkn),MHD_STATICSTR_LEN_(tkn)) | ||
1046 | |||
1047 | |||
1048 | /** | ||
1049 | * Are we allowed to keep the given connection alive? We can use the | ||
1050 | * TCP stream for a second request if the connection is HTTP 1.1 and | ||
1051 | * the "Connection" header either does not exist or is not set to | ||
1052 | * "close", or if the connection is HTTP 1.0 and the "Connection" | ||
1053 | * header is explicitly set to "keep-alive". If no HTTP version is | ||
1054 | * specified (or if it is not 1.0 or 1.1), we definitively close the | ||
1055 | * connection. If the "Connection" header is not exactly "close" or | ||
1056 | * "keep-alive", we proceed to use the default for the respective HTTP | ||
1057 | * version (which is conservative for HTTP 1.0, but might be a bit | ||
1058 | * optimistic for HTTP 1.1). | ||
1059 | * | ||
1060 | * @param request the request to check for keepalive | ||
1061 | * @return #MHD_YES if (based on the request), a keepalive is | ||
1062 | * legal | ||
1063 | */ | ||
1064 | static bool | ||
1065 | keepalive_possible (struct MHD_Request *request) | ||
1066 | { | ||
1067 | if (MHD_CONN_MUST_CLOSE == request->keepalive) | ||
1068 | return false; | ||
1069 | if (NULL == request->version_s) | ||
1070 | return false; | ||
1071 | if ( (NULL != request->response) && | ||
1072 | (request->response->v10_only) ) | ||
1073 | return false; | ||
1074 | |||
1075 | if (MHD_str_equal_caseless_ (request->version_s, | ||
1076 | MHD_HTTP_VERSION_1_1)) | ||
1077 | { | ||
1078 | if (MHD_lookup_header_s_token_ci (request, | ||
1079 | MHD_HTTP_HEADER_CONNECTION, | ||
1080 | "upgrade")) | ||
1081 | return false; | ||
1082 | if (MHD_lookup_header_s_token_ci (request, | ||
1083 | MHD_HTTP_HEADER_CONNECTION, | ||
1084 | "close")) | ||
1085 | return false; | ||
1086 | return true; | ||
1087 | } | ||
1088 | if (MHD_str_equal_caseless_ (request->version_s, | ||
1089 | MHD_HTTP_VERSION_1_0)) | ||
1090 | { | ||
1091 | if (MHD_lookup_header_s_token_ci (request, | ||
1092 | MHD_HTTP_HEADER_CONNECTION, | ||
1093 | "Keep-Alive")) | ||
1094 | return true; | ||
1095 | return false; | ||
1096 | } | ||
1097 | return false; | ||
1098 | } | ||
1099 | |||
1100 | |||
1101 | /** | ||
1102 | * Produce HTTP time stamp. | ||
1103 | * | ||
1104 | * @param date where to write the header, with | ||
1105 | * at least 128 bytes available space. | ||
1106 | * @param date_len number of bytes in @a date | ||
1107 | */ | ||
1108 | static void | ||
1109 | get_date_string (char *date, | ||
1110 | size_t date_len) | ||
1111 | { | ||
1112 | static const char *const days[] = { | ||
1113 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | ||
1114 | }; | ||
1115 | static const char *const mons[] = { | ||
1116 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | ||
1117 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | ||
1118 | }; | ||
1119 | struct tm now; | ||
1120 | time_t t; | ||
1121 | #if !defined(HAVE_C11_GMTIME_S) && !defined(HAVE_W32_GMTIME_S) && !defined(HAVE_GMTIME_R) | ||
1122 | struct tm* pNow; | ||
1123 | #endif | ||
1124 | |||
1125 | date[0] = 0; | ||
1126 | time (&t); | ||
1127 | #if defined(HAVE_C11_GMTIME_S) | ||
1128 | if (NULL == gmtime_s (&t, | ||
1129 | &now)) | ||
1130 | return; | ||
1131 | #elif defined(HAVE_W32_GMTIME_S) | ||
1132 | if (0 != gmtime_s (&now, | ||
1133 | &t)) | ||
1134 | return; | ||
1135 | #elif defined(HAVE_GMTIME_R) | ||
1136 | if (NULL == gmtime_r(&t, | ||
1137 | &now)) | ||
1138 | return; | ||
1139 | #else | ||
1140 | pNow = gmtime(&t); | ||
1141 | if (NULL == pNow) | ||
1142 | return; | ||
1143 | now = *pNow; | ||
1144 | #endif | ||
1145 | MHD_snprintf_ (date, | ||
1146 | date_len, | ||
1147 | "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n", | ||
1148 | days[now.tm_wday % 7], | ||
1149 | (unsigned int) now.tm_mday, | ||
1150 | mons[now.tm_mon % 12], | ||
1151 | (unsigned int) (1900 + now.tm_year), | ||
1152 | (unsigned int) now.tm_hour, | ||
1153 | (unsigned int) now.tm_min, | ||
1154 | (unsigned int) now.tm_sec); | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | /** | ||
1159 | * Check whether response header contains particular @a token. | ||
1160 | * | ||
1161 | * Token could be surrounded by spaces and tabs and delimited by comma. | ||
1162 | * Case-insensitive match used for header names and tokens. | ||
1163 | * @param response the response to query | ||
1164 | * @param key header name | ||
1165 | * @param token the token to find | ||
1166 | * @param token_len the length of token, not including optional | ||
1167 | * terminating null-character. | ||
1168 | * @return true if token is found in specified header, | ||
1169 | * false otherwise | ||
1170 | */ | ||
1171 | static bool | ||
1172 | check_response_header_token_ci (const struct MHD_Response *response, | ||
1173 | const char *key, | ||
1174 | const char *token, | ||
1175 | size_t token_len) | ||
1176 | { | ||
1177 | struct MHD_HTTP_Header *pos; | ||
1178 | |||
1179 | if ( (NULL == key) || | ||
1180 | ('\0' == key[0]) || | ||
1181 | (NULL == token) || | ||
1182 | ('\0' == token[0]) ) | ||
1183 | return false; | ||
1184 | |||
1185 | for (pos = response->first_header; | ||
1186 | NULL != pos; | ||
1187 | pos = pos->next) | ||
1188 | { | ||
1189 | if ( (pos->kind == MHD_HEADER_KIND) && | ||
1190 | MHD_str_equal_caseless_ (pos->header, | ||
1191 | key) && | ||
1192 | MHD_str_has_token_caseless_ (pos->value, | ||
1193 | token, | ||
1194 | token_len) ) | ||
1195 | return true; | ||
1196 | } | ||
1197 | return false; | ||
1198 | } | ||
1199 | |||
1200 | |||
1201 | /** | ||
1202 | * Check whether response header contains particular static @a tkn. | ||
1203 | * | ||
1204 | * Token could be surrounded by spaces and tabs and delimited by comma. | ||
1205 | * Case-insensitive match used for header names and tokens. | ||
1206 | * @param r the response to query | ||
1207 | * @param k header name | ||
1208 | * @param tkn the static string of token to find | ||
1209 | * @return true if token is found in specified header, | ||
1210 | * false otherwise | ||
1211 | */ | ||
1212 | #define check_response_header_s_token_ci(r,k,tkn) \ | ||
1213 | check_response_header_token_ci((r),(k),(tkn),MHD_STATICSTR_LEN_(tkn)) | ||
1214 | |||
1215 | |||
1216 | /** | ||
1217 | * Allocate the connection's write buffer and fill it with all of the | ||
1218 | * headers (or footers, if we have already sent the body) from the | ||
1219 | * HTTPd's response. If headers are missing in the response supplied | ||
1220 | * by the application, additional headers may be added here. | ||
1221 | * | ||
1222 | * @param request the request for which to build the response header | ||
1223 | * @return true on success, false on failure (out of memory) | ||
1224 | */ | ||
1225 | static bool | ||
1226 | build_header_response (struct MHD_Request *request) | ||
1227 | { | ||
1228 | struct MHD_Connection *connection = request->connection; | ||
1229 | struct MHD_Daemon *daemon = request->daemon; | ||
1230 | struct MHD_Response *response = request->response; | ||
1231 | size_t size; | ||
1232 | size_t off; | ||
1233 | struct MHD_HTTP_Header *pos; | ||
1234 | char code[256]; | ||
1235 | char date[128]; | ||
1236 | char content_length_buf[128]; | ||
1237 | size_t content_length_len; | ||
1238 | char *data; | ||
1239 | enum MHD_ValueKind kind; | ||
1240 | bool client_requested_close; | ||
1241 | bool response_has_close; | ||
1242 | bool response_has_keepalive; | ||
1243 | const char *have_encoding; | ||
1244 | const char *have_content_length; | ||
1245 | bool must_add_close; | ||
1246 | bool must_add_chunked_encoding; | ||
1247 | bool must_add_keep_alive; | ||
1248 | bool must_add_content_length; | ||
1249 | |||
1250 | mhd_assert (NULL != request->version_s); | ||
1251 | if (0 == request->version_s[0]) | ||
1252 | { | ||
1253 | data = MHD_pool_allocate (connection->pool, | ||
1254 | 0, | ||
1255 | MHD_YES); | ||
1256 | request->write_buffer = data; | ||
1257 | request->write_buffer_append_offset = 0; | ||
1258 | request->write_buffer_send_offset = 0; | ||
1259 | request->write_buffer_size = 0; | ||
1260 | return true; | ||
1261 | } | ||
1262 | if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) | ||
1263 | { | ||
1264 | const char *reason_phrase; | ||
1265 | const char *version; | ||
1266 | |||
1267 | reason_phrase | ||
1268 | = MHD_get_reason_phrase_for (response->status_code); | ||
1269 | version | ||
1270 | = (response->icy) | ||
1271 | ? "ICY" | ||
1272 | : ( (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_0, | ||
1273 | request->version_s)) | ||
1274 | ? MHD_HTTP_VERSION_1_0 | ||
1275 | : MHD_HTTP_VERSION_1_1); | ||
1276 | MHD_snprintf_ (code, | ||
1277 | sizeof (code), | ||
1278 | "%s %u %s\r\n", | ||
1279 | version, | ||
1280 | response->status_code, | ||
1281 | reason_phrase); | ||
1282 | off = strlen (code); | ||
1283 | /* estimate size */ | ||
1284 | size = off + 2; /* +2 for extra "\r\n" at the end */ | ||
1285 | kind = MHD_HEADER_KIND; | ||
1286 | if ( (! daemon->suppress_date) && | ||
1287 | (NULL == MHD_response_get_header (response, | ||
1288 | MHD_HTTP_HEADER_DATE)) ) | ||
1289 | get_date_string (date, | ||
1290 | sizeof (date)); | ||
1291 | else | ||
1292 | date[0] = '\0'; | ||
1293 | size += strlen (date); | ||
1294 | } | ||
1295 | else | ||
1296 | { | ||
1297 | /* 2 bytes for final CRLF of a Chunked-Body */ | ||
1298 | size = 2; | ||
1299 | kind = MHD_FOOTER_KIND; | ||
1300 | off = 0; | ||
1301 | } | ||
1302 | |||
1303 | /* calculate extra headers we need to add, such as 'Connection: close', | ||
1304 | first see what was explicitly requested by the application */ | ||
1305 | must_add_close = false; | ||
1306 | must_add_chunked_encoding = false; | ||
1307 | must_add_keep_alive = false; | ||
1308 | must_add_content_length = false; | ||
1309 | response_has_close = false; | ||
1310 | response_has_keepalive = false; | ||
1311 | switch (request->state) | ||
1312 | { | ||
1313 | case MHD_REQUEST_FOOTERS_RECEIVED: | ||
1314 | response_has_close | ||
1315 | = check_response_header_s_token_ci (response, | ||
1316 | MHD_HTTP_HEADER_CONNECTION, | ||
1317 | "close"); | ||
1318 | response_has_keepalive | ||
1319 | = check_response_header_s_token_ci (response, | ||
1320 | MHD_HTTP_HEADER_CONNECTION, | ||
1321 | "Keep-Alive"); | ||
1322 | client_requested_close | ||
1323 | = MHD_lookup_header_s_token_ci (request, | ||
1324 | MHD_HTTP_HEADER_CONNECTION, | ||
1325 | "close"); | ||
1326 | |||
1327 | if (response->v10_only) | ||
1328 | request->keepalive = MHD_CONN_MUST_CLOSE; | ||
1329 | #ifdef UPGRADE_SUPPORT | ||
1330 | else if (NULL != response->upgrade_handler) | ||
1331 | /* If this connection will not be "upgraded", it must be closed. */ | ||
1332 | request->keepalive = MHD_CONN_MUST_CLOSE; | ||
1333 | #endif /* UPGRADE_SUPPORT */ | ||
1334 | |||
1335 | /* now analyze chunked encoding situation */ | ||
1336 | request->have_chunked_upload = false; | ||
1337 | |||
1338 | if ( (MHD_SIZE_UNKNOWN == response->total_size) && | ||
1339 | #ifdef UPGRADE_SUPPORT | ||
1340 | (NULL == response->upgrade_handler) && | ||
1341 | #endif /* UPGRADE_SUPPORT */ | ||
1342 | (! response_has_close) && | ||
1343 | (! client_requested_close) ) | ||
1344 | { | ||
1345 | /* size is unknown, and close was not explicitly requested; | ||
1346 | need to either to HTTP 1.1 chunked encoding or | ||
1347 | close the connection */ | ||
1348 | /* 'close' header doesn't exist yet, see if we need to add one; | ||
1349 | if the client asked for a close, no need to start chunk'ing */ | ||
1350 | if ( (keepalive_possible (request)) && | ||
1351 | (MHD_str_equal_caseless_ (MHD_HTTP_VERSION_1_1, | ||
1352 | request->version_s)) ) | ||
1353 | { | ||
1354 | have_encoding | ||
1355 | = MHD_response_get_header (response, | ||
1356 | MHD_HTTP_HEADER_TRANSFER_ENCODING); | ||
1357 | if (NULL == have_encoding) | ||
1358 | { | ||
1359 | must_add_chunked_encoding = true; | ||
1360 | request->have_chunked_upload = true; | ||
1361 | } | ||
1362 | else if (MHD_str_equal_caseless_ (have_encoding, | ||
1363 | "identity")) | ||
1364 | { | ||
1365 | /* application forced identity encoding, can't do 'chunked' */ | ||
1366 | must_add_close = true; | ||
1367 | } | ||
1368 | else | ||
1369 | { | ||
1370 | request->have_chunked_upload = true; | ||
1371 | } | ||
1372 | } | ||
1373 | else | ||
1374 | { | ||
1375 | /* Keep alive or chunking not possible | ||
1376 | => set close header if not present */ | ||
1377 | if (! response_has_close) | ||
1378 | must_add_close = true; | ||
1379 | } | ||
1380 | } | ||
1381 | |||
1382 | /* check for other reasons to add 'close' header */ | ||
1383 | if ( ( (client_requested_close) || | ||
1384 | (connection->read_closed) || | ||
1385 | (MHD_CONN_MUST_CLOSE == request->keepalive)) && | ||
1386 | (! response_has_close) && | ||
1387 | #ifdef UPGRADE_SUPPORT | ||
1388 | (NULL == response->upgrade_handler) && | ||
1389 | #endif /* UPGRADE_SUPPORT */ | ||
1390 | (! response->v10_only) ) | ||
1391 | must_add_close = true; | ||
1392 | |||
1393 | /* check if we should add a 'content length' header */ | ||
1394 | have_content_length | ||
1395 | = MHD_response_get_header (response, | ||
1396 | MHD_HTTP_HEADER_CONTENT_LENGTH); | ||
1397 | |||
1398 | /* MHD_HTTP_NO_CONTENT, MHD_HTTP_NOT_MODIFIED and 1xx-status | ||
1399 | codes SHOULD NOT have a Content-Length according to spec; | ||
1400 | also chunked encoding / unknown length or CONNECT... */ | ||
1401 | if ( (MHD_SIZE_UNKNOWN != response->total_size) && | ||
1402 | (MHD_HTTP_NO_CONTENT != response->status_code) && | ||
1403 | (MHD_HTTP_NOT_MODIFIED != response->status_code) && | ||
1404 | (MHD_HTTP_OK <= response->status_code) && | ||
1405 | (NULL == have_content_length) && | ||
1406 | (request->method != MHD_METHOD_CONNECT) ) | ||
1407 | { | ||
1408 | /* | ||
1409 | Here we add a content-length if one is missing; however, | ||
1410 | for 'connect' methods, the responses MUST NOT include a | ||
1411 | content-length header *if* the response code is 2xx (in | ||
1412 | which case we expect there to be no body). Still, | ||
1413 | as we don't know the response code here in some cases, we | ||
1414 | simply only force adding a content-length header if this | ||
1415 | is not a 'connect' or if the response is not empty | ||
1416 | (which is kind of more sane, because if some crazy | ||
1417 | application did return content with a 2xx status code, | ||
1418 | then having a content-length might again be a good idea). | ||
1419 | |||
1420 | Note that the change from 'SHOULD NOT' to 'MUST NOT' is | ||
1421 | a recent development of the HTTP 1.1 specification. | ||
1422 | */ | ||
1423 | content_length_len | ||
1424 | = MHD_snprintf_ (content_length_buf, | ||
1425 | sizeof (content_length_buf), | ||
1426 | MHD_HTTP_HEADER_CONTENT_LENGTH ": " MHD_UNSIGNED_LONG_LONG_PRINTF "\r\n", | ||
1427 | (MHD_UNSIGNED_LONG_LONG) response->total_size); | ||
1428 | must_add_content_length = true; | ||
1429 | } | ||
1430 | |||
1431 | /* check for adding keep alive */ | ||
1432 | if ( (! response_has_keepalive) && | ||
1433 | (! response_has_close) && | ||
1434 | (! must_add_close) && | ||
1435 | (MHD_CONN_MUST_CLOSE != request->keepalive) && | ||
1436 | #ifdef UPGRADE_SUPPORT | ||
1437 | (NULL == response->upgrade_handler) && | ||
1438 | #endif /* UPGRADE_SUPPORT */ | ||
1439 | (keepalive_possible (request)) ) | ||
1440 | must_add_keep_alive = true; | ||
1441 | break; | ||
1442 | case MHD_REQUEST_BODY_SENT: | ||
1443 | response_has_keepalive = false; | ||
1444 | break; | ||
1445 | default: | ||
1446 | mhd_assert (0); | ||
1447 | } | ||
1448 | |||
1449 | if (MHD_CONN_MUST_CLOSE != request->keepalive) | ||
1450 | { | ||
1451 | if ( (must_add_close) || | ||
1452 | (response_has_close) ) | ||
1453 | request->keepalive = MHD_CONN_MUST_CLOSE; | ||
1454 | else if ( (must_add_keep_alive) || | ||
1455 | (response_has_keepalive) ) | ||
1456 | request->keepalive = MHD_CONN_USE_KEEPALIVE; | ||
1457 | } | ||
1458 | |||
1459 | if (must_add_close) | ||
1460 | size += MHD_STATICSTR_LEN_ ("Connection: close\r\n"); | ||
1461 | if (must_add_keep_alive) | ||
1462 | size += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n"); | ||
1463 | if (must_add_chunked_encoding) | ||
1464 | size += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n"); | ||
1465 | if (must_add_content_length) | ||
1466 | size += content_length_len; | ||
1467 | mhd_assert (! (must_add_close && must_add_keep_alive) ); | ||
1468 | mhd_assert (! (must_add_chunked_encoding && must_add_content_length) ); | ||
1469 | |||
1470 | for (pos = response->first_header; NULL != pos; pos = pos->next) | ||
1471 | { | ||
1472 | /* TODO: add proper support for excluding "Keep-Alive" token. */ | ||
1473 | if ( (pos->kind == kind) && | ||
1474 | (! ( (must_add_close) && | ||
1475 | (response_has_keepalive) && | ||
1476 | (MHD_str_equal_caseless_(pos->header, | ||
1477 | MHD_HTTP_HEADER_CONNECTION)) && | ||
1478 | (MHD_str_equal_caseless_(pos->value, | ||
1479 | "Keep-Alive")) ) ) ) | ||
1480 | size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, linefeeds */ | ||
1481 | } | ||
1482 | /* produce data */ | ||
1483 | data = MHD_pool_allocate (connection->pool, | ||
1484 | size + 1, | ||
1485 | MHD_NO); | ||
1486 | if (NULL == data) | ||
1487 | { | ||
1488 | #ifdef HAVE_MESSAGES | ||
1489 | MHD_DLOG (daemon, | ||
1490 | MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, | ||
1491 | "Not enough memory for write!\n"); | ||
1492 | #endif | ||
1493 | return false; | ||
1494 | } | ||
1495 | if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) | ||
1496 | { | ||
1497 | memcpy (data, | ||
1498 | code, | ||
1499 | off); | ||
1500 | } | ||
1501 | if (must_add_close) | ||
1502 | { | ||
1503 | /* we must add the 'Connection: close' header */ | ||
1504 | memcpy (&data[off], | ||
1505 | "Connection: close\r\n", | ||
1506 | MHD_STATICSTR_LEN_ ("Connection: close\r\n")); | ||
1507 | off += MHD_STATICSTR_LEN_ ("Connection: close\r\n"); | ||
1508 | } | ||
1509 | if (must_add_keep_alive) | ||
1510 | { | ||
1511 | /* we must add the 'Connection: Keep-Alive' header */ | ||
1512 | memcpy (&data[off], | ||
1513 | "Connection: Keep-Alive\r\n", | ||
1514 | MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n")); | ||
1515 | off += MHD_STATICSTR_LEN_ ("Connection: Keep-Alive\r\n"); | ||
1516 | } | ||
1517 | if (must_add_chunked_encoding) | ||
1518 | { | ||
1519 | /* we must add the 'Transfer-Encoding: chunked' header */ | ||
1520 | memcpy (&data[off], | ||
1521 | "Transfer-Encoding: chunked\r\n", | ||
1522 | MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n")); | ||
1523 | off += MHD_STATICSTR_LEN_ ("Transfer-Encoding: chunked\r\n"); | ||
1524 | } | ||
1525 | if (must_add_content_length) | ||
1526 | { | ||
1527 | /* we must add the 'Content-Length' header */ | ||
1528 | memcpy (&data[off], | ||
1529 | content_length_buf, | ||
1530 | content_length_len); | ||
1531 | off += content_length_len; | ||
1532 | } | ||
1533 | for (pos = response->first_header; NULL != pos; pos = pos->next) | ||
1534 | { | ||
1535 | /* TODO: add proper support for excluding "Keep-Alive" token. */ | ||
1536 | if ( (pos->kind == kind) && | ||
1537 | (! ( (must_add_close) && | ||
1538 | (response_has_keepalive) && | ||
1539 | (MHD_str_equal_caseless_(pos->header, | ||
1540 | MHD_HTTP_HEADER_CONNECTION)) && | ||
1541 | (MHD_str_equal_caseless_(pos->value, | ||
1542 | "Keep-Alive")) ) ) ) | ||
1543 | off += MHD_snprintf_ (&data[off], | ||
1544 | size - off, | ||
1545 | "%s: %s\r\n", | ||
1546 | pos->header, | ||
1547 | pos->value); | ||
1548 | } | ||
1549 | if (MHD_REQUEST_FOOTERS_RECEIVED == request->state) | ||
1550 | { | ||
1551 | strcpy (&data[off], | ||
1552 | date); | ||
1553 | off += strlen (date); | ||
1554 | } | ||
1555 | memcpy (&data[off], | ||
1556 | "\r\n", | ||
1557 | 2); | ||
1558 | off += 2; | ||
1559 | |||
1560 | if (off != size) | ||
1561 | mhd_panic (mhd_panic_cls, | ||
1562 | __FILE__, | ||
1563 | __LINE__, | ||
1564 | NULL); | ||
1565 | request->write_buffer = data; | ||
1566 | request->write_buffer_append_offset = size; | ||
1567 | request->write_buffer_send_offset = 0; | ||
1568 | request->write_buffer_size = size + 1; | ||
1569 | return true; | ||
1570 | } | ||
1571 | |||
1572 | |||
1573 | /** | ||
1574 | * We encountered an error processing the request. Handle it properly | ||
1575 | * by stopping to read data and sending the indicated response code | ||
1576 | * and message. | ||
1577 | * | ||
1578 | * @param request the request | ||
1579 | * @param ec error code for MHD | ||
1580 | * @param status_code the response code to send (400, 413 or 414) | ||
1581 | * @param message the error message to send | ||
1582 | */ | ||
1583 | static void | ||
1584 | transmit_error_response (struct MHD_Request *request, | ||
1585 | enum MHD_StatusCode ec, | ||
1586 | enum MHD_HTTP_StatusCode status_code, | ||
1587 | const char *message) | ||
1588 | { | ||
1589 | struct MHD_Response *response; | ||
1590 | |||
1591 | if (NULL == request->version_s) | ||
1592 | { | ||
1593 | /* we were unable to process the full header line, so we don't | ||
1594 | really know what version the client speaks; assume 1.0 */ | ||
1595 | request->version_s = MHD_HTTP_VERSION_1_0; | ||
1596 | } | ||
1597 | request->state = MHD_REQUEST_FOOTERS_RECEIVED; | ||
1598 | request->connection->read_closed = true; | ||
1599 | #ifdef HAVE_MESSAGES | ||
1600 | MHD_DLOG (request->daemon, | ||
1601 | ec, | ||
1602 | _("Error processing request (HTTP response code is %u (`%s')). Closing connection.\n"), | ||
1603 | status_code, | ||
1604 | message); | ||
1605 | #endif | ||
1606 | if (NULL != request->response) | ||
1607 | { | ||
1608 | MHD_response_queue_for_destroy (request->response); | ||
1609 | request->response = NULL; | ||
1610 | } | ||
1611 | response = MHD_response_from_buffer (status_code, | ||
1612 | strlen (message), | ||
1613 | (void *) message, | ||
1614 | MHD_RESPMEM_PERSISTENT); | ||
1615 | request->response = response; | ||
1616 | /* Do not reuse this connection. */ | ||
1617 | request->keepalive = MHD_CONN_MUST_CLOSE; | ||
1618 | if (! build_header_response (request)) | ||
1619 | { | ||
1620 | /* oops - close! */ | ||
1621 | CONNECTION_CLOSE_ERROR (request->connection, | ||
1622 | ec, | ||
1623 | _("Closing connection (failed to create response header)\n")); | ||
1624 | } | ||
1625 | else | ||
1626 | { | ||
1627 | request->state = MHD_REQUEST_HEADERS_SENDING; | ||
1628 | } | ||
935 | } | 1629 | } |
936 | 1630 | ||
937 | 1631 | ||
@@ -999,6 +1693,43 @@ method_string_to_enum (const char *method) | |||
999 | 1693 | ||
1000 | 1694 | ||
1001 | /** | 1695 | /** |
1696 | * Add an entry to the HTTP headers of a request. If this fails, | ||
1697 | * transmit an error response (request too big). | ||
1698 | * | ||
1699 | * @param request the request for which a value should be set | ||
1700 | * @param kind kind of the value | ||
1701 | * @param key key for the value | ||
1702 | * @param value the value itself | ||
1703 | * @return false on failure (out of memory), true for success | ||
1704 | */ | ||
1705 | static bool | ||
1706 | request_add_header (struct MHD_Request *request, | ||
1707 | const char *key, | ||
1708 | const char *value, | ||
1709 | enum MHD_ValueKind kind) | ||
1710 | { | ||
1711 | if (MHD_NO == | ||
1712 | MHD_request_set_value (request, | ||
1713 | kind, | ||
1714 | key, | ||
1715 | value)) | ||
1716 | { | ||
1717 | #ifdef HAVE_MESSAGES | ||
1718 | MHD_DLOG (request->daemon, | ||
1719 | MHD_SC_CONNECTION_POOL_MALLOC_FAILURE, | ||
1720 | _("Not enough memory in pool to allocate header record!\n")); | ||
1721 | #endif | ||
1722 | transmit_error_response (request, | ||
1723 | MHD_SC_CLIENT_HEADER_TOO_BIG, | ||
1724 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | ||
1725 | REQUEST_TOO_BIG); | ||
1726 | return false; | ||
1727 | } | ||
1728 | return true; | ||
1729 | } | ||
1730 | |||
1731 | |||
1732 | /** | ||
1002 | * Parse the first line of the HTTP HEADER. | 1733 | * Parse the first line of the HTTP HEADER. |
1003 | * | 1734 | * |
1004 | * @param connection the connection (updated) | 1735 | * @param connection the connection (updated) |
@@ -1011,7 +1742,6 @@ parse_initial_message_line (struct MHD_Request *request, | |||
1011 | char *line, | 1742 | char *line, |
1012 | size_t line_len) | 1743 | size_t line_len) |
1013 | { | 1744 | { |
1014 | struct MHD_Connection *connection = request->connection; | ||
1015 | struct MHD_Daemon *daemon = request->daemon; | 1745 | struct MHD_Daemon *daemon = request->daemon; |
1016 | const char *curi; | 1746 | const char *curi; |
1017 | char *uri; | 1747 | char *uri; |
@@ -1036,7 +1766,7 @@ parse_initial_message_line (struct MHD_Request *request, | |||
1036 | { | 1766 | { |
1037 | curi = ""; | 1767 | curi = ""; |
1038 | uri = NULL; | 1768 | uri = NULL; |
1039 | request->version = ""; | 1769 | request->version_s = ""; |
1040 | args = NULL; | 1770 | args = NULL; |
1041 | } | 1771 | } |
1042 | else | 1772 | else |
@@ -1055,22 +1785,21 @@ parse_initial_message_line (struct MHD_Request *request, | |||
1055 | if (http_version > uri) | 1785 | if (http_version > uri) |
1056 | { | 1786 | { |
1057 | http_version[0] = '\0'; | 1787 | http_version[0] = '\0'; |
1058 | request->version = http_version + 1; | 1788 | request->version_s = http_version + 1; |
1059 | args = memchr (uri, | 1789 | args = memchr (uri, |
1060 | '?', | 1790 | '?', |
1061 | http_version - uri); | 1791 | http_version - uri); |
1062 | } | 1792 | } |
1063 | else | 1793 | else |
1064 | { | 1794 | { |
1065 | request->version = ""; | 1795 | request->version_s = ""; |
1066 | args = memchr (uri, | 1796 | args = memchr (uri, |
1067 | '?', | 1797 | '?', |
1068 | line_len - (uri - line)); | 1798 | line_len - (uri - line)); |
1069 | } | 1799 | } |
1070 | } | 1800 | } |
1071 | if (NULL != daemon->uri_log_callback) | 1801 | if (NULL != daemon->early_uri_logger_cb) |
1072 | { | 1802 | { |
1073 | request->client_aware = true; | ||
1074 | request->client_context | 1803 | request->client_context |
1075 | = daemon->early_uri_logger_cb (daemon->early_uri_logger_cb_cls, | 1804 | = daemon->early_uri_logger_cb (daemon->early_uri_logger_cb_cls, |
1076 | curi, | 1805 | curi, |
@@ -1081,10 +1810,10 @@ parse_initial_message_line (struct MHD_Request *request, | |||
1081 | args[0] = '\0'; | 1810 | args[0] = '\0'; |
1082 | args++; | 1811 | args++; |
1083 | /* note that this call clobbers 'args' */ | 1812 | /* note that this call clobbers 'args' */ |
1084 | MHD_parse_arguments_ (connection, | 1813 | MHD_parse_arguments_ (request, |
1085 | MHD_GET_ARGUMENT_KIND, | 1814 | MHD_GET_ARGUMENT_KIND, |
1086 | args, | 1815 | args, |
1087 | &connection_add_header, | 1816 | &request_add_header, |
1088 | &unused_num_headers); | 1817 | &unused_num_headers); |
1089 | } | 1818 | } |
1090 | if (NULL != uri) | 1819 | if (NULL != uri) |
@@ -1092,41 +1821,6 @@ parse_initial_message_line (struct MHD_Request *request, | |||
1092 | request, | 1821 | request, |
1093 | uri); | 1822 | uri); |
1094 | request->url = curi; | 1823 | request->url = curi; |
1095 | return MHD_YES; | ||
1096 | } | ||
1097 | |||
1098 | |||
1099 | /** | ||
1100 | * Add an entry to the HTTP headers of a request. If this fails, | ||
1101 | * transmit an error response (request too big). | ||
1102 | * | ||
1103 | * @param request the request for which a value should be set | ||
1104 | * @param kind kind of the value | ||
1105 | * @param key key for the value | ||
1106 | * @param value the value itself | ||
1107 | * @return false on failure (out of memory), true for success | ||
1108 | */ | ||
1109 | static bool | ||
1110 | connection_add_header (struct MHD_Request *request, | ||
1111 | const char *key, | ||
1112 | const char *value, | ||
1113 | enum MHD_ValueKind kind) | ||
1114 | { | ||
1115 | if (MHD_NO == | ||
1116 | MHD_request_set_value (request, | ||
1117 | kind, | ||
1118 | key, | ||
1119 | value)) | ||
1120 | { | ||
1121 | #ifdef HAVE_MESSAGES | ||
1122 | MHD_DLOG (request->daemon, | ||
1123 | _("Not enough memory in pool to allocate header record!\n")); | ||
1124 | #endif | ||
1125 | transmit_error_response (request->connection, | ||
1126 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | ||
1127 | REQUEST_TOO_BIG); | ||
1128 | return false; | ||
1129 | } | ||
1130 | return true; | 1824 | return true; |
1131 | } | 1825 | } |
1132 | 1826 | ||
@@ -1148,7 +1842,8 @@ process_header_line (struct MHD_Request *request, | |||
1148 | char *colon; | 1842 | char *colon; |
1149 | 1843 | ||
1150 | /* line should be normal header line, find colon */ | 1844 | /* line should be normal header line, find colon */ |
1151 | colon = strchr (line, ':'); | 1845 | colon = strchr (line, |
1846 | ':'); | ||
1152 | if (NULL == colon) | 1847 | if (NULL == colon) |
1153 | { | 1848 | { |
1154 | /* error in header line, die hard */ | 1849 | /* error in header line, die hard */ |
@@ -1157,7 +1852,7 @@ process_header_line (struct MHD_Request *request, | |||
1157 | _("Received malformed line (no colon). Closing connection.\n")); | 1852 | _("Received malformed line (no colon). Closing connection.\n")); |
1158 | return false; | 1853 | return false; |
1159 | } | 1854 | } |
1160 | if (-1 >= request->daemon->strict_for_client) | 1855 | if (MHD_PSL_PERMISSIVE != request->daemon->protocol_strict_level) |
1161 | { | 1856 | { |
1162 | /* check for whitespace before colon, which is not allowed | 1857 | /* check for whitespace before colon, which is not allowed |
1163 | by RFC 7230 section 3.2.4; we count space ' ' and | 1858 | by RFC 7230 section 3.2.4; we count space ' ' and |
@@ -1252,7 +1947,8 @@ process_broken_line (struct MHD_Request *request, | |||
1252 | last_len + tmp_len + 1); | 1947 | last_len + tmp_len + 1); |
1253 | if (NULL == last) | 1948 | if (NULL == last) |
1254 | { | 1949 | { |
1255 | transmit_error_response (connection, | 1950 | transmit_error_response (request, |
1951 | MHD_SC_CLIENT_HEADER_TOO_BIG, | ||
1256 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | 1952 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, |
1257 | REQUEST_TOO_BIG); | 1953 | REQUEST_TOO_BIG); |
1258 | return MHD_NO; | 1954 | return MHD_NO; |
@@ -1270,7 +1966,8 @@ process_broken_line (struct MHD_Request *request, | |||
1270 | request->colon, | 1966 | request->colon, |
1271 | kind)) | 1967 | kind)) |
1272 | { | 1968 | { |
1273 | transmit_error_response (connection, | 1969 | transmit_error_response (request, |
1970 | MHD_SC_CLIENT_HEADER_TOO_BIG, | ||
1274 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | 1971 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, |
1275 | REQUEST_TOO_BIG); | 1972 | REQUEST_TOO_BIG); |
1276 | return false; | 1973 | return false; |
@@ -1281,7 +1978,8 @@ process_broken_line (struct MHD_Request *request, | |||
1281 | if (! process_header_line (request, | 1978 | if (! process_header_line (request, |
1282 | line)) | 1979 | line)) |
1283 | { | 1980 | { |
1284 | transmit_error_response (connection, | 1981 | transmit_error_response (request, |
1982 | MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, | ||
1285 | MHD_HTTP_BAD_REQUEST, | 1983 | MHD_HTTP_BAD_REQUEST, |
1286 | REQUEST_MALFORMED); | 1984 | REQUEST_MALFORMED); |
1287 | return false; | 1985 | return false; |
@@ -1291,7 +1989,782 @@ process_broken_line (struct MHD_Request *request, | |||
1291 | } | 1989 | } |
1292 | 1990 | ||
1293 | 1991 | ||
1294 | #ifdef REWRITE_IN_PROGRESS | 1992 | /** |
1993 | * Parse a single line of the HTTP header. Advance read_buffer (!) | ||
1994 | * appropriately. If the current line does not fit, consider growing | ||
1995 | * the buffer. If the line is far too long, close the connection. If | ||
1996 | * no line is found (incomplete, buffer too small, line too long), | ||
1997 | * return NULL. Otherwise return a pointer to the line. | ||
1998 | * | ||
1999 | * @param request request we're processing | ||
2000 | * @param[out] line_len pointer to variable that receive | ||
2001 | * length of line or NULL | ||
2002 | * @return NULL if no full line is available; note that the returned | ||
2003 | * string will not be 0-termianted | ||
2004 | */ | ||
2005 | static char * | ||
2006 | get_next_header_line (struct MHD_Request *request, | ||
2007 | size_t *line_len) | ||
2008 | { | ||
2009 | char *rbuf; | ||
2010 | size_t pos; | ||
2011 | |||
2012 | if (0 == request->read_buffer_offset) | ||
2013 | return NULL; | ||
2014 | pos = 0; | ||
2015 | rbuf = request->read_buffer; | ||
2016 | while ( (pos < request->read_buffer_offset - 1) && | ||
2017 | ('\r' != rbuf[pos]) && | ||
2018 | ('\n' != rbuf[pos]) ) | ||
2019 | pos++; | ||
2020 | if ( (pos == request->read_buffer_offset - 1) && | ||
2021 | ('\n' != rbuf[pos]) ) | ||
2022 | { | ||
2023 | /* not found, consider growing... */ | ||
2024 | if ( (request->read_buffer_offset == request->read_buffer_size) && | ||
2025 | (! try_grow_read_buffer (request)) ) | ||
2026 | { | ||
2027 | transmit_error_response (request, | ||
2028 | MHD_SC_CLIENT_HEADER_TOO_BIG, | ||
2029 | (NULL != request->url) | ||
2030 | ? MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE | ||
2031 | : MHD_HTTP_URI_TOO_LONG, | ||
2032 | REQUEST_TOO_BIG); | ||
2033 | } | ||
2034 | if (line_len) | ||
2035 | *line_len = 0; | ||
2036 | return NULL; | ||
2037 | } | ||
2038 | |||
2039 | if (line_len) | ||
2040 | *line_len = pos; | ||
2041 | /* found, check if we have proper LFCR */ | ||
2042 | if ( ('\r' == rbuf[pos]) && | ||
2043 | ('\n' == rbuf[pos + 1]) ) | ||
2044 | rbuf[pos++] = '\0'; /* skip both r and n */ | ||
2045 | rbuf[pos++] = '\0'; | ||
2046 | request->read_buffer += pos; | ||
2047 | request->read_buffer_size -= pos; | ||
2048 | request->read_buffer_offset -= pos; | ||
2049 | return rbuf; | ||
2050 | } | ||
2051 | |||
2052 | |||
2053 | /** | ||
2054 | * Check whether is possible to force push socket buffer content as | ||
2055 | * partial packet. | ||
2056 | * MHD use different buffering logic depending on whether flushing of | ||
2057 | * socket buffer is possible or not. | ||
2058 | * If flushing IS possible than MHD activates extra buffering before | ||
2059 | * sending data to prevent sending partial packets and flush pending | ||
2060 | * data in socket buffer to push last partial packet to client after | ||
2061 | * sending logical completed part of data (for example: after sending | ||
2062 | * full response header or full response message). | ||
2063 | * If flushing IS NOT possible than MHD activates no buffering (no | ||
2064 | * delay sending) when it going to send formed fully completed logical | ||
2065 | * part of data and activate normal buffering after sending. | ||
2066 | * For idled keep-alive connection MHD always activate normal | ||
2067 | * buffering. | ||
2068 | * | ||
2069 | * @param connection connection to check | ||
2070 | * @return true if force push is possible, false otherwise | ||
2071 | */ | ||
2072 | static bool | ||
2073 | socket_flush_possible(struct MHD_Connection *connection) | ||
2074 | { | ||
2075 | (void) connection; /* Mute compiler warning. */ | ||
2076 | #if defined(TCP_CORK) || defined(TCP_PUSH) | ||
2077 | return true; | ||
2078 | #else /* !TCP_CORK && !TCP_PUSH */ | ||
2079 | return false; | ||
2080 | #endif /* !TCP_CORK && !TCP_PUSH */ | ||
2081 | } | ||
2082 | |||
2083 | |||
2084 | /** | ||
2085 | * Activate extra buffering mode on connection socket to prevent | ||
2086 | * sending of partial packets. | ||
2087 | * | ||
2088 | * @param connection connection to be processed | ||
2089 | * @return true on success, false otherwise | ||
2090 | */ | ||
2091 | static bool | ||
2092 | socket_start_extra_buffering (struct MHD_Connection *connection) | ||
2093 | { | ||
2094 | bool res = false; | ||
2095 | (void)connection; /* Mute compiler warning. */ | ||
2096 | #if defined(TCP_CORK) || defined(TCP_NOPUSH) | ||
2097 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
2098 | #if defined(TCP_NODELAY) | ||
2099 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
2100 | #endif /* TCP_NODELAY */ | ||
2101 | mhd_assert(NULL != connection); | ||
2102 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
2103 | /* Buffer data before sending */ | ||
2104 | res = (0 == setsockopt (connection->socket_fd, | ||
2105 | IPPROTO_TCP, | ||
2106 | TCP_NOPUSH, | ||
2107 | (const void *) &on_val, | ||
2108 | sizeof (on_val))) | ||
2109 | ? true : false; | ||
2110 | #if defined(TCP_NODELAY) | ||
2111 | /* Enable Nagle's algorithm */ | ||
2112 | /* TCP_NODELAY may interfere with TCP_NOPUSH */ | ||
2113 | res &= (0 == setsockopt (connection->socket_fd, | ||
2114 | IPPROTO_TCP, | ||
2115 | TCP_NODELAY, | ||
2116 | (const void *) &off_val, | ||
2117 | sizeof (off_val))) | ||
2118 | ? true : false; | ||
2119 | #endif /* TCP_NODELAY */ | ||
2120 | #else /* TCP_CORK */ | ||
2121 | #if defined(TCP_NODELAY) | ||
2122 | /* Enable Nagle's algorithm */ | ||
2123 | /* TCP_NODELAY may prevent enabling TCP_CORK. Resulting buffering mode depends | ||
2124 | solely on TCP_CORK result, so ignoring return code here. */ | ||
2125 | (void) setsockopt (connection->socket_fd, | ||
2126 | IPPROTO_TCP, | ||
2127 | TCP_NODELAY, | ||
2128 | (const void *) &off_val, | ||
2129 | sizeof (off_val)); | ||
2130 | #endif /* TCP_NODELAY */ | ||
2131 | /* Send only full packets */ | ||
2132 | res = (0 == setsockopt (connection->socket_fd, | ||
2133 | IPPROTO_TCP, | ||
2134 | TCP_CORK, | ||
2135 | (const void *) &on_val, | ||
2136 | sizeof (on_val))) | ||
2137 | ? true : false; | ||
2138 | #endif /* TCP_CORK */ | ||
2139 | #endif /* TCP_CORK || TCP_NOPUSH */ | ||
2140 | return res; | ||
2141 | } | ||
2142 | |||
2143 | |||
2144 | /** | ||
2145 | * Activate no buffering mode (no delay sending) on connection socket. | ||
2146 | * | ||
2147 | * @param connection connection to be processed | ||
2148 | * @return true on success, false otherwise | ||
2149 | */ | ||
2150 | static bool | ||
2151 | socket_start_no_buffering (struct MHD_Connection *connection) | ||
2152 | { | ||
2153 | #if defined(TCP_NODELAY) | ||
2154 | bool res = true; | ||
2155 | const MHD_SCKT_OPT_BOOL_ on_val = 1; | ||
2156 | #if defined(TCP_CORK) || defined(TCP_NOPUSH) | ||
2157 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
2158 | #endif /* TCP_CORK || TCP_NOPUSH */ | ||
2159 | |||
2160 | (void)connection; /* Mute compiler warning. */ | ||
2161 | mhd_assert(NULL != connection); | ||
2162 | #if defined(TCP_CORK) | ||
2163 | /* Allow partial packets */ | ||
2164 | res &= (0 == setsockopt (connection->socket_fd, | ||
2165 | IPPROTO_TCP, | ||
2166 | TCP_CORK, | ||
2167 | (const void *) &off_val, | ||
2168 | sizeof (off_val))) | ||
2169 | ? true : false; | ||
2170 | #endif /* TCP_CORK */ | ||
2171 | #if defined(TCP_NODELAY) | ||
2172 | /* Disable Nagle's algorithm for sending packets without delay */ | ||
2173 | res &= (0 == setsockopt (connection->socket_fd, | ||
2174 | IPPROTO_TCP, | ||
2175 | TCP_NODELAY, | ||
2176 | (const void *) &on_val, | ||
2177 | sizeof (on_val))) | ||
2178 | ? true : false; | ||
2179 | #endif /* TCP_NODELAY */ | ||
2180 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
2181 | /* Disable extra buffering */ | ||
2182 | res &= (0 == setsockopt (connection->socket_fd, | ||
2183 | IPPROTO_TCP, | ||
2184 | TCP_NOPUSH, | ||
2185 | (const void *) &off_val, | ||
2186 | sizeof (off_val))) | ||
2187 | ? true : false; | ||
2188 | #endif /* TCP_NOPUSH && !TCP_CORK */ | ||
2189 | return res; | ||
2190 | #else /* !TCP_NODELAY */ | ||
2191 | return false; | ||
2192 | #endif /* !TCP_NODELAY */ | ||
2193 | } | ||
2194 | |||
2195 | |||
2196 | /** | ||
2197 | * Activate no buffering mode (no delay sending) on connection socket | ||
2198 | * and push to client data pending in socket buffer. | ||
2199 | * | ||
2200 | * @param connection connection to be processed | ||
2201 | * @return true on success, false otherwise | ||
2202 | */ | ||
2203 | static bool | ||
2204 | socket_start_no_buffering_flush (struct MHD_Connection *connection) | ||
2205 | { | ||
2206 | bool res = true; | ||
2207 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
2208 | const int dummy = 0; | ||
2209 | #endif /* !TCP_CORK */ | ||
2210 | |||
2211 | if (NULL == connection) | ||
2212 | return false; /* FIXME: use MHD_NONNULL? */ | ||
2213 | res = socket_start_no_buffering (connection); | ||
2214 | #if defined(TCP_NOPUSH) && !defined(TCP_CORK) | ||
2215 | /* Force flush data with zero send otherwise Darwin and some BSD systems | ||
2216 | will add 5 seconds delay. Not required with TCP_CORK as switching off | ||
2217 | TCP_CORK always flushes socket buffer. */ | ||
2218 | res &= (0 <= MHD_send_ (connection->socket_fd, | ||
2219 | &dummy, | ||
2220 | 0)) | ||
2221 | ? true : false; | ||
2222 | #endif /* TCP_NOPUSH && !TCP_CORK*/ | ||
2223 | return res; | ||
2224 | } | ||
2225 | |||
2226 | |||
2227 | /** | ||
2228 | * Activate normal buffering mode on connection socket. | ||
2229 | * | ||
2230 | * @param connection connection to be processed | ||
2231 | * @return true on success, false otherwise | ||
2232 | */ | ||
2233 | static bool | ||
2234 | socket_start_normal_buffering (struct MHD_Connection *connection) | ||
2235 | { | ||
2236 | #if defined(TCP_NODELAY) | ||
2237 | bool res = true; | ||
2238 | const MHD_SCKT_OPT_BOOL_ off_val = 0; | ||
2239 | #if defined(TCP_CORK) | ||
2240 | MHD_SCKT_OPT_BOOL_ cork_val = 0; | ||
2241 | socklen_t param_size = sizeof (cork_val); | ||
2242 | #endif /* TCP_CORK */ | ||
2243 | |||
2244 | mhd_assert(NULL != connection); | ||
2245 | #if defined(TCP_CORK) | ||
2246 | /* Allow partial packets */ | ||
2247 | /* Disabling TCP_CORK will flush partial packet even if TCP_CORK wasn't enabled before | ||
2248 | so try to check current value of TCP_CORK to prevent unrequested flushing */ | ||
2249 | if ( (0 != getsockopt (connection->socket_fd, | ||
2250 | IPPROTO_TCP, | ||
2251 | TCP_CORK, | ||
2252 | (void*)&cork_val, | ||
2253 | ¶m_size)) || | ||
2254 | (0 != cork_val)) | ||
2255 | res &= (0 == setsockopt (connection->socket_fd, | ||
2256 | IPPROTO_TCP, | ||
2257 | TCP_CORK, | ||
2258 | (const void *) &off_val, | ||
2259 | sizeof (off_val))) | ||
2260 | ? true : false; | ||
2261 | #elif defined(TCP_NOPUSH) | ||
2262 | /* Disable extra buffering */ | ||
2263 | /* No need to check current value as disabling TCP_NOPUSH will not flush partial | ||
2264 | packet if TCP_NOPUSH wasn't enabled before */ | ||
2265 | res &= (0 == setsockopt (connection->socket_fd, | ||
2266 | IPPROTO_TCP, | ||
2267 | TCP_NOPUSH, | ||
2268 | (const void *) &off_val, | ||
2269 | sizeof (off_val))) | ||
2270 | ? true : false; | ||
2271 | #endif /* TCP_NOPUSH && !TCP_CORK */ | ||
2272 | /* Enable Nagle's algorithm for normal buffering */ | ||
2273 | res &= (0 == setsockopt (connection->socket_fd, | ||
2274 | IPPROTO_TCP, | ||
2275 | TCP_NODELAY, | ||
2276 | (const void *) &off_val, | ||
2277 | sizeof (off_val))) | ||
2278 | ? true : false; | ||
2279 | return res; | ||
2280 | #else /* !TCP_NODELAY */ | ||
2281 | return false; | ||
2282 | #endif /* !TCP_NODELAY */ | ||
2283 | } | ||
2284 | |||
2285 | |||
2286 | /** | ||
2287 | * Do we (still) need to send a 100 continue | ||
2288 | * message for this request? | ||
2289 | * | ||
2290 | * @param request the request to test | ||
2291 | * @return false if we don't need 100 CONTINUE, true if we do | ||
2292 | */ | ||
2293 | static bool | ||
2294 | need_100_continue (struct MHD_Request *request) | ||
2295 | { | ||
2296 | const char *expect; | ||
2297 | |||
2298 | return ( (NULL == request->response) && | ||
2299 | (NULL != request->version_s) && | ||
2300 | (MHD_str_equal_caseless_(request->version_s, | ||
2301 | MHD_HTTP_VERSION_1_1)) && | ||
2302 | (NULL != (expect = MHD_request_lookup_value (request, | ||
2303 | MHD_HEADER_KIND, | ||
2304 | MHD_HTTP_HEADER_EXPECT))) && | ||
2305 | (MHD_str_equal_caseless_(expect, | ||
2306 | "100-continue")) && | ||
2307 | (request->continue_message_write_offset < | ||
2308 | MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) ); | ||
2309 | } | ||
2310 | |||
2311 | |||
2312 | /** | ||
2313 | * Parse the cookie header (see RFC 2109). | ||
2314 | * | ||
2315 | * @param request request to parse header of | ||
2316 | * @return true for success, false for failure (malformed, out of memory) | ||
2317 | */ | ||
2318 | static int | ||
2319 | parse_cookie_header (struct MHD_Request *request) | ||
2320 | { | ||
2321 | const char *hdr; | ||
2322 | char *cpy; | ||
2323 | char *pos; | ||
2324 | char *sce; | ||
2325 | char *semicolon; | ||
2326 | char *equals; | ||
2327 | char *ekill; | ||
2328 | char old; | ||
2329 | int quotes; | ||
2330 | |||
2331 | hdr = MHD_request_lookup_value (request, | ||
2332 | MHD_HEADER_KIND, | ||
2333 | MHD_HTTP_HEADER_COOKIE); | ||
2334 | if (NULL == hdr) | ||
2335 | return true; | ||
2336 | cpy = MHD_pool_allocate (request->connection->pool, | ||
2337 | strlen (hdr) + 1, | ||
2338 | MHD_YES); | ||
2339 | if (NULL == cpy) | ||
2340 | { | ||
2341 | #ifdef HAVE_MESSAGES | ||
2342 | MHD_DLOG (request->daemon, | ||
2343 | MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE, | ||
2344 | _("Not enough memory in pool to parse cookies!\n")); | ||
2345 | #endif | ||
2346 | transmit_error_response (request, | ||
2347 | MHD_SC_COOKIE_POOL_ALLOCATION_FAILURE, | ||
2348 | MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, | ||
2349 | REQUEST_TOO_BIG); | ||
2350 | return false; | ||
2351 | } | ||
2352 | memcpy (cpy, | ||
2353 | hdr, | ||
2354 | strlen (hdr) + 1); | ||
2355 | pos = cpy; | ||
2356 | while (NULL != pos) | ||
2357 | { | ||
2358 | while (' ' == *pos) | ||
2359 | pos++; /* skip spaces */ | ||
2360 | |||
2361 | sce = pos; | ||
2362 | while ( ((*sce) != '\0') && | ||
2363 | ((*sce) != ',') && | ||
2364 | ((*sce) != ';') && | ||
2365 | ((*sce) != '=') ) | ||
2366 | sce++; | ||
2367 | /* remove tailing whitespace (if any) from key */ | ||
2368 | ekill = sce - 1; | ||
2369 | while ( (*ekill == ' ') && | ||
2370 | (ekill >= pos) ) | ||
2371 | *(ekill--) = '\0'; | ||
2372 | old = *sce; | ||
2373 | *sce = '\0'; | ||
2374 | if (old != '=') | ||
2375 | { | ||
2376 | /* value part omitted, use empty string... */ | ||
2377 | if (! request_add_header (request, | ||
2378 | pos, | ||
2379 | "", | ||
2380 | MHD_COOKIE_KIND)) | ||
2381 | return false; | ||
2382 | if (old == '\0') | ||
2383 | break; | ||
2384 | pos = sce + 1; | ||
2385 | continue; | ||
2386 | } | ||
2387 | equals = sce + 1; | ||
2388 | quotes = 0; | ||
2389 | semicolon = equals; | ||
2390 | while ( ('\0' != semicolon[0]) && | ||
2391 | ( (0 != quotes) || | ||
2392 | ( (';' != semicolon[0]) && | ||
2393 | (',' != semicolon[0]) ) ) ) | ||
2394 | { | ||
2395 | if ('"' == semicolon[0]) | ||
2396 | quotes = (quotes + 1) & 1; | ||
2397 | semicolon++; | ||
2398 | } | ||
2399 | if ('\0' == semicolon[0]) | ||
2400 | semicolon = NULL; | ||
2401 | if (NULL != semicolon) | ||
2402 | { | ||
2403 | semicolon[0] = '\0'; | ||
2404 | semicolon++; | ||
2405 | } | ||
2406 | /* remove quotes */ | ||
2407 | if ( ('"' == equals[0]) && | ||
2408 | ('"' == equals[strlen (equals) - 1]) ) | ||
2409 | { | ||
2410 | equals[strlen (equals) - 1] = '\0'; | ||
2411 | equals++; | ||
2412 | } | ||
2413 | if (! request_add_header (request, | ||
2414 | pos, | ||
2415 | equals, | ||
2416 | MHD_COOKIE_KIND)) | ||
2417 | return false; | ||
2418 | pos = semicolon; | ||
2419 | } | ||
2420 | return true; | ||
2421 | } | ||
2422 | |||
2423 | |||
2424 | /** | ||
2425 | * Parse the various headers; figure out the size | ||
2426 | * of the upload and make sure the headers follow | ||
2427 | * the protocol. Advance to the appropriate state. | ||
2428 | * | ||
2429 | * @param request request we're processing | ||
2430 | */ | ||
2431 | static void | ||
2432 | parse_request_headers (struct MHD_Request *request) | ||
2433 | { | ||
2434 | struct MHD_Daemon *daemon = request->daemon; | ||
2435 | struct MHD_Connection *connection = request->connection; | ||
2436 | const char *clen; | ||
2437 | struct MHD_Response *response; | ||
2438 | const char *enc; | ||
2439 | const char *end; | ||
2440 | |||
2441 | parse_cookie_header (request); /* FIXME: return value ignored! */ | ||
2442 | if ( (MHD_PSL_STRICT == daemon->protocol_strict_level) && | ||
2443 | (NULL != request->version_s) && | ||
2444 | (MHD_str_equal_caseless_(MHD_HTTP_VERSION_1_1, | ||
2445 | request->version_s)) && | ||
2446 | (NULL == | ||
2447 | MHD_request_lookup_value (request, | ||
2448 | MHD_HEADER_KIND, | ||
2449 | MHD_HTTP_HEADER_HOST)) ) | ||
2450 | { | ||
2451 | /* die, http 1.1 request without host and we are pedantic */ | ||
2452 | request->state = MHD_REQUEST_FOOTERS_RECEIVED; | ||
2453 | connection->read_closed = true; | ||
2454 | #ifdef HAVE_MESSAGES | ||
2455 | MHD_DLOG (daemon, | ||
2456 | MHD_SC_HOST_HEADER_MISSING, | ||
2457 | _("Received HTTP 1.1 request without `Host' header.\n")); | ||
2458 | #endif | ||
2459 | mhd_assert (NULL == request->response); | ||
2460 | response = | ||
2461 | MHD_response_from_buffer (MHD_HTTP_BAD_REQUEST, | ||
2462 | MHD_STATICSTR_LEN_ (REQUEST_LACKS_HOST), | ||
2463 | REQUEST_LACKS_HOST, | ||
2464 | MHD_RESPMEM_PERSISTENT); | ||
2465 | request->response = response; | ||
2466 | // FIXME: state machine advance? | ||
2467 | return; | ||
2468 | } | ||
2469 | |||
2470 | request->remaining_upload_size = 0; | ||
2471 | enc = MHD_request_lookup_value (request, | ||
2472 | MHD_HEADER_KIND, | ||
2473 | MHD_HTTP_HEADER_TRANSFER_ENCODING); | ||
2474 | if (NULL != enc) | ||
2475 | { | ||
2476 | request->remaining_upload_size = MHD_SIZE_UNKNOWN; | ||
2477 | if (MHD_str_equal_caseless_ (enc, | ||
2478 | "chunked")) | ||
2479 | request->have_chunked_upload = true; | ||
2480 | return; | ||
2481 | } | ||
2482 | clen = MHD_request_lookup_value (request, | ||
2483 | MHD_HEADER_KIND, | ||
2484 | MHD_HTTP_HEADER_CONTENT_LENGTH); | ||
2485 | if (NULL == clen) | ||
2486 | return; | ||
2487 | end = clen + MHD_str_to_uint64_ (clen, | ||
2488 | &request->remaining_upload_size); | ||
2489 | if ( (clen == end) || | ||
2490 | ('\0' != *end) ) | ||
2491 | { | ||
2492 | request->remaining_upload_size = 0; | ||
2493 | #ifdef HAVE_MESSAGES | ||
2494 | MHD_DLOG (request->daemon, | ||
2495 | MHD_SC_CONTENT_LENGTH_MALFORMED, | ||
2496 | "Failed to parse `Content-Length' header. Closing connection.\n"); | ||
2497 | #endif | ||
2498 | CONNECTION_CLOSE_ERROR (connection, | ||
2499 | MHD_SC_CONTENT_LENGTH_MALFORMED, | ||
2500 | NULL); | ||
2501 | } | ||
2502 | } | ||
2503 | |||
2504 | |||
2505 | /** | ||
2506 | * Call the handler of the application for this | ||
2507 | * request. | ||
2508 | * | ||
2509 | * @param request request we're processing | ||
2510 | */ | ||
2511 | static void | ||
2512 | call_request_handler (struct MHD_Request *request) | ||
2513 | { | ||
2514 | struct MHD_Daemon *daemon = request->daemon; | ||
2515 | struct MHD_Connection *connection = request->connection; | ||
2516 | const struct MHD_Action *action; | ||
2517 | |||
2518 | if (NULL != request->response) | ||
2519 | return; /* already queued a response */ | ||
2520 | if (NULL == (action = | ||
2521 | daemon->rc (daemon->rc_cls, | ||
2522 | request, | ||
2523 | request->url, | ||
2524 | request->method))) | ||
2525 | { | ||
2526 | /* serious internal error, close connection */ | ||
2527 | CONNECTION_CLOSE_ERROR (connection, | ||
2528 | MHD_SC_APPLICATION_CALLBACK_FAILURE_CLOSED, | ||
2529 | _("Application reported internal error, closing connection.\n")); | ||
2530 | return; | ||
2531 | } | ||
2532 | action->action (action->action_cls, | ||
2533 | request); | ||
2534 | } | ||
2535 | |||
2536 | |||
2537 | /** | ||
2538 | * Call the handler of the application for this request. Handles | ||
2539 | * chunking of the upload as well as normal uploads. | ||
2540 | * | ||
2541 | * @param request request we're processing | ||
2542 | */ | ||
2543 | static void | ||
2544 | process_request_body (struct MHD_Request *request) | ||
2545 | { | ||
2546 | struct MHD_Daemon *daemon = request->daemon; | ||
2547 | struct MHD_Connection *connection = request->connection; | ||
2548 | size_t available; | ||
2549 | bool instant_retry; | ||
2550 | char *buffer_head; | ||
2551 | |||
2552 | if (NULL != request->response) | ||
2553 | return; /* already queued a response */ | ||
2554 | |||
2555 | buffer_head = request->read_buffer; | ||
2556 | available = request->read_buffer_offset; | ||
2557 | do | ||
2558 | { | ||
2559 | size_t to_be_processed; | ||
2560 | size_t left_unprocessed; | ||
2561 | size_t processed_size; | ||
2562 | |||
2563 | instant_retry = false; | ||
2564 | if ( (request->have_chunked_upload) && | ||
2565 | (MHD_SIZE_UNKNOWN == request->remaining_upload_size) ) | ||
2566 | { | ||
2567 | if ( (request->current_chunk_offset == request->current_chunk_size) && | ||
2568 | (0LLU != request->current_chunk_offset) && | ||
2569 | (available >= 2) ) | ||
2570 | { | ||
2571 | size_t i; | ||
2572 | |||
2573 | /* skip new line at the *end* of a chunk */ | ||
2574 | i = 0; | ||
2575 | if ( ('\r' == buffer_head[i]) || | ||
2576 | ('\n' == buffer_head[i]) ) | ||
2577 | i++; /* skip 1st part of line feed */ | ||
2578 | if ( ('\r' == buffer_head[i]) || | ||
2579 | ('\n' == buffer_head[i]) ) | ||
2580 | i++; /* skip 2nd part of line feed */ | ||
2581 | if (0 == i) | ||
2582 | { | ||
2583 | /* malformed encoding */ | ||
2584 | CONNECTION_CLOSE_ERROR (connection, | ||
2585 | MHD_SC_CHUNKED_ENCODING_MALFORMED, | ||
2586 | _("Received malformed HTTP request (bad chunked encoding). Closing connection.\n")); | ||
2587 | return; | ||
2588 | } | ||
2589 | available -= i; | ||
2590 | buffer_head += i; | ||
2591 | request->current_chunk_offset = 0; | ||
2592 | request->current_chunk_size = 0; | ||
2593 | } | ||
2594 | if (request->current_chunk_offset < | ||
2595 | request->current_chunk_size) | ||
2596 | { | ||
2597 | uint64_t cur_chunk_left; | ||
2598 | |||
2599 | /* we are in the middle of a chunk, give | ||
2600 | as much as possible to the client (without | ||
2601 | crossing chunk boundaries) */ | ||
2602 | cur_chunk_left | ||
2603 | = request->current_chunk_size - request->current_chunk_offset; | ||
2604 | if (cur_chunk_left > available) | ||
2605 | { | ||
2606 | to_be_processed = available; | ||
2607 | } | ||
2608 | else | ||
2609 | { /* cur_chunk_left <= (size_t)available */ | ||
2610 | to_be_processed = (size_t)cur_chunk_left; | ||
2611 | if (available > to_be_processed) | ||
2612 | instant_retry = true; | ||
2613 | } | ||
2614 | } | ||
2615 | else | ||
2616 | { | ||
2617 | size_t i; | ||
2618 | size_t end_size; | ||
2619 | bool malformed; | ||
2620 | |||
2621 | /* we need to read chunk boundaries */ | ||
2622 | i = 0; | ||
2623 | while (i < available) | ||
2624 | { | ||
2625 | if ( ('\r' == buffer_head[i]) || | ||
2626 | ('\n' == buffer_head[i]) || | ||
2627 | (';' == buffer_head[i]) ) | ||
2628 | break; | ||
2629 | i++; | ||
2630 | if (i >= 16) | ||
2631 | break; | ||
2632 | } | ||
2633 | end_size = i; | ||
2634 | /* find beginning of CRLF (skip over chunk extensions) */ | ||
2635 | if (';' == buffer_head[i]) | ||
2636 | { | ||
2637 | while (i < available) | ||
2638 | { | ||
2639 | if ( ('\r' == buffer_head[i]) || | ||
2640 | ('\n' == buffer_head[i]) ) | ||
2641 | break; | ||
2642 | i++; | ||
2643 | } | ||
2644 | } | ||
2645 | /* take '\n' into account; if '\n' is the unavailable | ||
2646 | character, we will need to wait until we have it | ||
2647 | before going further */ | ||
2648 | if ( (i + 1 >= available) && | ||
2649 | ! ( (1 == i) && | ||
2650 | (2 == available) && | ||
2651 | ('0' == buffer_head[0]) ) ) | ||
2652 | break; /* need more data... */ | ||
2653 | i++; | ||
2654 | malformed = (end_size >= 16); | ||
2655 | if (! malformed) | ||
2656 | { | ||
2657 | size_t num_dig = MHD_strx_to_uint64_n_ (buffer_head, | ||
2658 | end_size, | ||
2659 | &request->current_chunk_size); | ||
2660 | malformed = (end_size != num_dig); | ||
2661 | } | ||
2662 | if (malformed) | ||
2663 | { | ||
2664 | /* malformed encoding */ | ||
2665 | CONNECTION_CLOSE_ERROR (connection, | ||
2666 | MHD_SC_CHUNKED_ENCODING_MALFORMED, | ||
2667 | _("Received malformed HTTP request (bad chunked encoding). Closing connection.\n")); | ||
2668 | return; | ||
2669 | } | ||
2670 | /* skip 2nd part of line feed */ | ||
2671 | if ( (i < available) && | ||
2672 | ( ('\r' == buffer_head[i]) || | ||
2673 | ('\n' == buffer_head[i]) ) ) | ||
2674 | i++; | ||
2675 | |||
2676 | buffer_head += i; | ||
2677 | available -= i; | ||
2678 | request->current_chunk_offset = 0; | ||
2679 | |||
2680 | if (available > 0) | ||
2681 | instant_retry = true; | ||
2682 | if (0LLU == request->current_chunk_size) | ||
2683 | { | ||
2684 | request->remaining_upload_size = 0; | ||
2685 | break; | ||
2686 | } | ||
2687 | continue; | ||
2688 | } | ||
2689 | } | ||
2690 | else | ||
2691 | { | ||
2692 | /* no chunked encoding, give all to the client */ | ||
2693 | if ( (0 != request->remaining_upload_size) && | ||
2694 | (MHD_SIZE_UNKNOWN != request->remaining_upload_size) && | ||
2695 | (request->remaining_upload_size < available) ) | ||
2696 | { | ||
2697 | to_be_processed = (size_t)request->remaining_upload_size; | ||
2698 | } | ||
2699 | else | ||
2700 | { | ||
2701 | /** | ||
2702 | * 1. no chunked encoding, give all to the client | ||
2703 | * 2. client may send large chunked data, but only a smaller part is available at one time. | ||
2704 | */ | ||
2705 | to_be_processed = available; | ||
2706 | } | ||
2707 | } | ||
2708 | left_unprocessed = to_be_processed; | ||
2709 | #if FIXME_OLD_STYLE | ||
2710 | if (MHD_NO == | ||
2711 | daemon->rc (daemon->rc_cls, | ||
2712 | request, | ||
2713 | request->url, | ||
2714 | request->method, | ||
2715 | request->version, | ||
2716 | buffer_head, | ||
2717 | &left_unprocessed, | ||
2718 | &request->client_context)) | ||
2719 | { | ||
2720 | /* serious internal error, close connection */ | ||
2721 | CONNECTION_CLOSE_ERROR (connection, | ||
2722 | MHD_SC_APPLICATION_CALLBACK_FAILURE_CLOSED, | ||
2723 | _("Application reported internal error, closing connection.\n")); | ||
2724 | return; | ||
2725 | } | ||
2726 | #endif | ||
2727 | if (left_unprocessed > to_be_processed) | ||
2728 | mhd_panic (mhd_panic_cls, | ||
2729 | __FILE__, | ||
2730 | __LINE__ | ||
2731 | #ifdef HAVE_MESSAGES | ||
2732 | , _("libmicrohttpd API violation") | ||
2733 | #else | ||
2734 | , NULL | ||
2735 | #endif | ||
2736 | ); | ||
2737 | if (0 != left_unprocessed) | ||
2738 | { | ||
2739 | instant_retry = false; /* client did not process everything */ | ||
2740 | #ifdef HAVE_MESSAGES | ||
2741 | /* client did not process all upload data, complain if | ||
2742 | the setup was incorrect, which may prevent us from | ||
2743 | handling the rest of the request */ | ||
2744 | if ( (MHD_TM_EXTERNAL_EVENT_LOOP == daemon->threading_model) && | ||
2745 | (! connection->suspended) ) | ||
2746 | MHD_DLOG (daemon, | ||
2747 | MHD_SC_APPLICATION_HUNG_CONNECTION, | ||
2748 | _("WARNING: incomplete upload processing and connection not suspended may result in hung connection.\n")); | ||
2749 | #endif | ||
2750 | } | ||
2751 | processed_size = to_be_processed - left_unprocessed; | ||
2752 | if (request->have_chunked_upload) | ||
2753 | request->current_chunk_offset += processed_size; | ||
2754 | /* dh left "processed" bytes in buffer for next time... */ | ||
2755 | buffer_head += processed_size; | ||
2756 | available -= processed_size; | ||
2757 | if (MHD_SIZE_UNKNOWN != request->remaining_upload_size) | ||
2758 | request->remaining_upload_size -= processed_size; | ||
2759 | } | ||
2760 | while (instant_retry); | ||
2761 | if (available > 0) | ||
2762 | memmove (request->read_buffer, | ||
2763 | buffer_head, | ||
2764 | available); | ||
2765 | request->read_buffer_offset = available; | ||
2766 | } | ||
2767 | |||
1295 | 2768 | ||
1296 | /** | 2769 | /** |
1297 | * This function was created to handle per-request processing that | 2770 | * This function was created to handle per-request processing that |
@@ -1304,13 +2777,13 @@ process_broken_line (struct MHD_Request *request, | |||
1304 | * request (not dead yet), false if it died | 2777 | * request (not dead yet), false if it died |
1305 | */ | 2778 | */ |
1306 | bool | 2779 | bool |
1307 | MHD_request_handle_idle (struct MHD_Request *request) | 2780 | MHD_request_handle_idle_ (struct MHD_Request *request) |
1308 | { | 2781 | { |
1309 | struct MHD_Daemon *daemon = request->daemon; | 2782 | struct MHD_Daemon *daemon = request->daemon; |
1310 | struct MHD_Connection *connection = request->connection; | 2783 | struct MHD_Connection *connection = request->connection; |
1311 | char *line; | 2784 | char *line; |
1312 | size_t line_len; | 2785 | size_t line_len; |
1313 | int ret; | 2786 | bool ret; |
1314 | 2787 | ||
1315 | request->in_idle = true; | 2788 | request->in_idle = true; |
1316 | while (! connection->suspended) | 2789 | while (! connection->suspended) |
@@ -1358,6 +2831,7 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1358 | line, | 2831 | line, |
1359 | line_len)) | 2832 | line_len)) |
1360 | CONNECTION_CLOSE_ERROR (connection, | 2833 | CONNECTION_CLOSE_ERROR (connection, |
2834 | MHD_SC_CONNECTION_CLOSED, | ||
1361 | NULL); | 2835 | NULL); |
1362 | else | 2836 | else |
1363 | request->state = MHD_REQUEST_URL_RECEIVED; | 2837 | request->state = MHD_REQUEST_URL_RECEIVED; |
@@ -1387,7 +2861,8 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1387 | if (! process_header_line (request, | 2861 | if (! process_header_line (request, |
1388 | line)) | 2862 | line)) |
1389 | { | 2863 | { |
1390 | transmit_error_response (connection, | 2864 | transmit_error_response (request, |
2865 | MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, | ||
1391 | MHD_HTTP_BAD_REQUEST, | 2866 | MHD_HTTP_BAD_REQUEST, |
1392 | REQUEST_MALFORMED); | 2867 | REQUEST_MALFORMED); |
1393 | break; | 2868 | break; |
@@ -1444,10 +2919,8 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1444 | break; | 2919 | break; |
1445 | } | 2920 | } |
1446 | if ( (NULL != request->response) && | 2921 | if ( (NULL != request->response) && |
1447 | ( (MHD_str_equal_caseless_ (request->method, | 2922 | ( (MHD_METHOD_POST == request->method) || |
1448 | MHD_HTTP_METHOD_POST)) || | 2923 | (MHD_METHOD_PUT == request->method) ) ) |
1449 | (MHD_str_equal_caseless_ (request->method, | ||
1450 | MHD_HTTP_METHOD_PUT))) ) | ||
1451 | { | 2924 | { |
1452 | /* we refused (no upload allowed!) */ | 2925 | /* we refused (no upload allowed!) */ |
1453 | request->remaining_upload_size = 0; | 2926 | request->remaining_upload_size = 0; |
@@ -1455,7 +2928,8 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1455 | connection->read_closed = true; | 2928 | connection->read_closed = true; |
1456 | } | 2929 | } |
1457 | request->state = (0 == request->remaining_upload_size) | 2930 | request->state = (0 == request->remaining_upload_size) |
1458 | ? MHD_REQUEST_FOOTERS_RECEIVED : MHD_REQUEST_CONTINUE_SENT; | 2931 | ? MHD_REQUEST_FOOTERS_RECEIVED |
2932 | : MHD_REQUEST_CONTINUE_SENT; | ||
1459 | if (connection->suspended) | 2933 | if (connection->suspended) |
1460 | break; | 2934 | break; |
1461 | continue; | 2935 | continue; |
@@ -1464,11 +2938,10 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1464 | MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) | 2938 | MHD_STATICSTR_LEN_ (HTTP_100_CONTINUE)) |
1465 | { | 2939 | { |
1466 | request->state = MHD_REQUEST_CONTINUE_SENT; | 2940 | request->state = MHD_REQUEST_CONTINUE_SENT; |
1467 | if (MHD_NO != socket_flush_possible (request)) | 2941 | if (! socket_flush_possible (connection)) |
1468 | socket_start_no_buffering_flush (request); | 2942 | socket_start_no_buffering_flush (connection); |
1469 | else | 2943 | else |
1470 | socket_start_normal_buffering (request); | 2944 | socket_start_normal_buffering (connection); |
1471 | |||
1472 | continue; | 2945 | continue; |
1473 | } | 2946 | } |
1474 | break; | 2947 | break; |
@@ -1482,10 +2955,10 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1482 | if ( (0 == request->remaining_upload_size) || | 2955 | if ( (0 == request->remaining_upload_size) || |
1483 | ( (MHD_SIZE_UNKNOWN == request->remaining_upload_size) && | 2956 | ( (MHD_SIZE_UNKNOWN == request->remaining_upload_size) && |
1484 | (0 == request->read_buffer_offset) && | 2957 | (0 == request->read_buffer_offset) && |
1485 | (request->read_closed) ) ) | 2958 | (connection->read_closed) ) ) |
1486 | { | 2959 | { |
1487 | if ( (request->have_chunked_upload) && | 2960 | if ( (request->have_chunked_upload) && |
1488 | (! request->read_closed) ) | 2961 | (! connection->read_closed) ) |
1489 | request->state = MHD_REQUEST_BODY_RECEIVED; | 2962 | request->state = MHD_REQUEST_BODY_RECEIVED; |
1490 | else | 2963 | else |
1491 | request->state = MHD_REQUEST_FOOTERS_RECEIVED; | 2964 | request->state = MHD_REQUEST_FOOTERS_RECEIVED; |
@@ -1504,6 +2977,7 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1504 | if (connection->read_closed) | 2977 | if (connection->read_closed) |
1505 | { | 2978 | { |
1506 | CONNECTION_CLOSE_ERROR (connection, | 2979 | CONNECTION_CLOSE_ERROR (connection, |
2980 | MHD_SC_CONNECTION_CLOSED, | ||
1507 | NULL); | 2981 | NULL); |
1508 | continue; | 2982 | continue; |
1509 | } | 2983 | } |
@@ -1519,7 +2993,8 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1519 | if (MHD_NO == process_header_line (request, | 2993 | if (MHD_NO == process_header_line (request, |
1520 | line)) | 2994 | line)) |
1521 | { | 2995 | { |
1522 | transmit_error_response (connection, | 2996 | transmit_error_response (request, |
2997 | MHD_SC_CONNECTION_PARSE_FAIL_CLOSED, | ||
1523 | MHD_HTTP_BAD_REQUEST, | 2998 | MHD_HTTP_BAD_REQUEST, |
1524 | REQUEST_MALFORMED); | 2999 | REQUEST_MALFORMED); |
1525 | break; | 3000 | break; |
@@ -1536,6 +3011,7 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1536 | if (connection->read_closed) | 3011 | if (connection->read_closed) |
1537 | { | 3012 | { |
1538 | CONNECTION_CLOSE_ERROR (connection, | 3013 | CONNECTION_CLOSE_ERROR (connection, |
3014 | MHD_SC_CONNECTION_CLOSED, | ||
1539 | NULL); | 3015 | NULL); |
1540 | continue; | 3016 | continue; |
1541 | } | 3017 | } |
@@ -1560,10 +3036,11 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1560 | continue; | 3036 | continue; |
1561 | if (NULL == request->response) | 3037 | if (NULL == request->response) |
1562 | break; /* try again next time */ | 3038 | break; /* try again next time */ |
1563 | if (MHD_NO == build_header_response (request)) | 3039 | if (! build_header_response (request)) |
1564 | { | 3040 | { |
1565 | /* oops - close! */ | 3041 | /* oops - close! */ |
1566 | CONNECTION_CLOSE_ERROR (connection, | 3042 | CONNECTION_CLOSE_ERROR (connection, |
3043 | MHD_SC_FAILED_RESPONSE_HEADER_GENERATION, | ||
1567 | _("Closing connection (failed to create response header)\n")); | 3044 | _("Closing connection (failed to create response header)\n")); |
1568 | continue; | 3045 | continue; |
1569 | } | 3046 | } |
@@ -1588,12 +3065,12 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1588 | socket_start_normal_buffering (connection); | 3065 | socket_start_normal_buffering (connection); |
1589 | request->state = MHD_REQUEST_UPGRADE; | 3066 | request->state = MHD_REQUEST_UPGRADE; |
1590 | /* This request is "upgraded". Pass socket to application. */ | 3067 | /* This request is "upgraded". Pass socket to application. */ |
1591 | if (MHD_YES != | 3068 | if (! MHD_response_execute_upgrade_ (request->response, |
1592 | MHD_response_execute_upgrade_ (request->response, | 3069 | request)) |
1593 | request)) | ||
1594 | { | 3070 | { |
1595 | /* upgrade failed, fail hard */ | 3071 | /* upgrade failed, fail hard */ |
1596 | CONNECTION_CLOSE_ERROR (connection, | 3072 | CONNECTION_CLOSE_ERROR (connection, |
3073 | MHD_SC_CONNECTION_CLOSED, | ||
1597 | NULL); | 3074 | NULL); |
1598 | continue; | 3075 | continue; |
1599 | } | 3076 | } |
@@ -1602,7 +3079,7 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1602 | { | 3079 | { |
1603 | struct MHD_Response * const resp = request->response; | 3080 | struct MHD_Response * const resp = request->response; |
1604 | request->response = NULL; | 3081 | request->response = NULL; |
1605 | MHD_destroy_response (resp); | 3082 | MHD_response_queue_for_destroy (resp); |
1606 | } | 3083 | } |
1607 | continue; | 3084 | continue; |
1608 | } | 3085 | } |
@@ -1671,10 +3148,11 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1671 | /* mutex was already unlocked by try_ready_chunked_body */ | 3148 | /* mutex was already unlocked by try_ready_chunked_body */ |
1672 | break; | 3149 | break; |
1673 | case MHD_REQUEST_BODY_SENT: | 3150 | case MHD_REQUEST_BODY_SENT: |
1674 | if (MHD_NO == build_header_response (request)) | 3151 | if (! build_header_response (request)) |
1675 | { | 3152 | { |
1676 | /* oops - close! */ | 3153 | /* oops - close! */ |
1677 | CONNECTION_CLOSE_ERROR (connection, | 3154 | CONNECTION_CLOSE_ERROR (connection, |
3155 | MHD_SC_FAILED_RESPONSE_HEADER_GENERATION, | ||
1678 | _("Closing connection (failed to create response header)\n")); | 3156 | _("Closing connection (failed to create response header)\n")); |
1679 | continue; | 3157 | continue; |
1680 | } | 3158 | } |
@@ -1689,32 +3167,33 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1689 | /* no default action */ | 3167 | /* no default action */ |
1690 | break; | 3168 | break; |
1691 | case MHD_REQUEST_FOOTERS_SENT: | 3169 | case MHD_REQUEST_FOOTERS_SENT: |
1692 | if (MHD_HTTP_PROCESSING == request->responseCode) | ||
1693 | { | 3170 | { |
1694 | /* After this type of response, we allow sending another! */ | 3171 | struct MHD_Response *response = request->response; |
1695 | request->state = MHD_REQUEST_HEADERS_PROCESSED; | 3172 | |
1696 | MHD_destroy_response (request->response); | 3173 | if (MHD_HTTP_PROCESSING == response->status_code) |
3174 | { | ||
3175 | /* After this type of response, we allow sending another! */ | ||
3176 | request->state = MHD_REQUEST_HEADERS_PROCESSED; | ||
3177 | MHD_response_queue_for_destroy (response); | ||
3178 | request->response = NULL; | ||
3179 | /* FIXME: maybe partially reset memory pool? */ | ||
3180 | continue; | ||
3181 | } | ||
3182 | if (socket_flush_possible (connection)) | ||
3183 | socket_start_no_buffering_flush (connection); | ||
3184 | else | ||
3185 | socket_start_normal_buffering (connection); | ||
3186 | |||
3187 | if (NULL != response->termination_cb) | ||
3188 | { | ||
3189 | response->termination_cb (response->termination_cb_cls, | ||
3190 | MHD_REQUEST_TERMINATED_COMPLETED_OK, | ||
3191 | request->client_context); | ||
3192 | } | ||
3193 | MHD_response_queue_for_destroy (response); | ||
1697 | request->response = NULL; | 3194 | request->response = NULL; |
1698 | /* FIXME: maybe partially reset memory pool? */ | ||
1699 | continue; | ||
1700 | } | 3195 | } |
1701 | if (MHD_NO != socket_flush_possible (connection)) | 3196 | if ( (MHD_CONN_USE_KEEPALIVE != request->keepalive) || |
1702 | socket_start_no_buffering_flush (connection); | ||
1703 | else | ||
1704 | socket_start_normal_buffering (connection); | ||
1705 | |||
1706 | MHD_destroy_response (request->response); | ||
1707 | connection->response = NULL; | ||
1708 | if ( (NULL != daemon->notify_completed) && | ||
1709 | (connection->client_aware) ) | ||
1710 | { | ||
1711 | connection->client_aware = false; | ||
1712 | daemon->notify_completed (daemon->notify_completed_cls, | ||
1713 | connection, | ||
1714 | &connection->client_context, | ||
1715 | MHD_REQUEST_TERMINATED_COMPLETED_OK); | ||
1716 | } | ||
1717 | if ( (MHD_CONN_USE_KEEPALIVE != request->keepalive) || | ||
1718 | (connection->read_closed) ) | 3197 | (connection->read_closed) ) |
1719 | { | 3198 | { |
1720 | /* have to close for some reason */ | 3199 | /* have to close for some reason */ |
@@ -1729,9 +3208,9 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1729 | else | 3208 | else |
1730 | { | 3209 | { |
1731 | /* can try to keep-alive */ | 3210 | /* can try to keep-alive */ |
1732 | if (MHD_NO != socket_flush_possible (connection)) | 3211 | if (socket_flush_possible (connection)) |
1733 | socket_start_normal_buffering (connection); | 3212 | socket_start_normal_buffering (connection); |
1734 | request->version = NULL; | 3213 | request->version_s = NULL; |
1735 | request->state = MHD_REQUEST_INIT; | 3214 | request->state = MHD_REQUEST_INIT; |
1736 | request->last = NULL; | 3215 | request->last = NULL; |
1737 | request->colon = NULL; | 3216 | request->colon = NULL; |
@@ -1743,10 +3222,12 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1743 | = MHD_pool_reset (connection->pool, | 3222 | = MHD_pool_reset (connection->pool, |
1744 | request->read_buffer, | 3223 | request->read_buffer, |
1745 | request->read_buffer_offset, | 3224 | request->read_buffer_offset, |
1746 | daemon->pool_size / 2); | 3225 | daemon->connection_memory_limit_b / 2); |
1747 | request->read_buffer_size | 3226 | request->read_buffer_size |
1748 | = daemon->pool_size / 2; | 3227 | = daemon->connection_memory_limit_b / 2; |
1749 | } | 3228 | } |
3229 | // FIXME: this is too much, NULLs out some of the things | ||
3230 | // initialized above... | ||
1750 | memset (&request, | 3231 | memset (&request, |
1751 | 0, | 3232 | 0, |
1752 | sizeof (struct MHD_Request)); | 3233 | sizeof (struct MHD_Request)); |
@@ -1756,11 +3237,11 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1756 | case MHD_REQUEST_CLOSED: | 3237 | case MHD_REQUEST_CLOSED: |
1757 | cleanup_connection (connection); | 3238 | cleanup_connection (connection); |
1758 | request->in_idle = false; | 3239 | request->in_idle = false; |
1759 | return MHD_NO; | 3240 | return false; |
1760 | #ifdef UPGRADE_SUPPORT | 3241 | #ifdef UPGRADE_SUPPORT |
1761 | case MHD_REQUEST_UPGRADE: | 3242 | case MHD_REQUEST_UPGRADE: |
1762 | request->in_idle = false; | 3243 | request->in_idle = false; |
1763 | return MHD_YES; /* keep open */ | 3244 | return true; /* keep open */ |
1764 | #endif /* UPGRADE_SUPPORT */ | 3245 | #endif /* UPGRADE_SUPPORT */ |
1765 | default: | 3246 | default: |
1766 | mhd_assert (0); | 3247 | mhd_assert (0); |
@@ -1778,14 +3259,14 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1778 | MHD_connection_close_ (connection, | 3259 | MHD_connection_close_ (connection, |
1779 | MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); | 3260 | MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); |
1780 | request->in_idle = false; | 3261 | request->in_idle = false; |
1781 | return MHD_YES; | 3262 | return true; |
1782 | } | 3263 | } |
1783 | } | 3264 | } |
1784 | MHD_connection_update_event_loop_info (connection); | 3265 | MHD_connection_update_event_loop_info (connection); |
1785 | ret = MHD_YES; | 3266 | ret = true; |
1786 | #ifdef EPOLL_SUPPORT | 3267 | #ifdef EPOLL_SUPPORT |
1787 | if ( (! connection->suspended) && | 3268 | if ( (! connection->suspended) && |
1788 | (0 != (daemon->options & MHD_USE_EPOLL)) ) | 3269 | (MHD_ELS_EPOLL == daemon->event_loop_syscall) ) |
1789 | { | 3270 | { |
1790 | ret = MHD_connection_epoll_update_ (connection); | 3271 | ret = MHD_connection_epoll_update_ (connection); |
1791 | } | 3272 | } |
@@ -1794,9 +3275,6 @@ MHD_request_handle_idle (struct MHD_Request *request) | |||
1794 | return ret; | 3275 | return ret; |
1795 | } | 3276 | } |
1796 | 3277 | ||
1797 | // rewrite commented out | ||
1798 | #endif | ||
1799 | |||
1800 | 3278 | ||
1801 | /** | 3279 | /** |
1802 | * Call the handlers for a connection in the appropriate order based | 3280 | * Call the handlers for a connection in the appropriate order based |
@@ -1835,7 +3313,7 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, | |||
1835 | read_ready) | 3313 | read_ready) |
1836 | { | 3314 | { |
1837 | MHD_request_handle_read_ (&con->request); | 3315 | MHD_request_handle_read_ (&con->request); |
1838 | ret = MHD_connection_handle_idle (con); | 3316 | ret = MHD_request_handle_idle_ (&con->request); |
1839 | states_info_processed = true; | 3317 | states_info_processed = true; |
1840 | } | 3318 | } |
1841 | /* No need to check value of 'ret' here as closed connection | 3319 | /* No need to check value of 'ret' here as closed connection |
@@ -1845,7 +3323,7 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, | |||
1845 | write_ready) | 3323 | write_ready) |
1846 | { | 3324 | { |
1847 | MHD_request_handle_write_ (&con->request); | 3325 | MHD_request_handle_write_ (&con->request); |
1848 | ret = MHD_connection_handle_idle (con); | 3326 | ret = MHD_request_handle_idle_ (&con->request); |
1849 | states_info_processed = true; | 3327 | states_info_processed = true; |
1850 | } | 3328 | } |
1851 | } | 3329 | } |
@@ -1853,17 +3331,17 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, | |||
1853 | { | 3331 | { |
1854 | MHD_connection_close_ (con, | 3332 | MHD_connection_close_ (con, |
1855 | MHD_REQUEST_TERMINATED_WITH_ERROR); | 3333 | MHD_REQUEST_TERMINATED_WITH_ERROR); |
1856 | return MHD_connection_handle_idle (con); | 3334 | return MHD_request_handle_idle_ (&con->request); |
1857 | } | 3335 | } |
1858 | 3336 | ||
1859 | if (! states_info_processed) | 3337 | if (! states_info_processed) |
1860 | { /* Connection is not read or write ready, but external conditions | 3338 | { /* Connection is not read or write ready, but external conditions |
1861 | * may be changed and need to be processed. */ | 3339 | * may be changed and need to be processed. */ |
1862 | ret = MHD_connection_handle_idle (con); | 3340 | ret = MHD_request_handle_idle_ (&con->request); |
1863 | } | 3341 | } |
1864 | /* Fast track for fast connections. */ | 3342 | /* Fast track for fast connections. */ |
1865 | /* If full request was read by single read_handler() invocation | 3343 | /* If full request was read by single read_handler() invocation |
1866 | and headers were completely prepared by single MHD_connection_handle_idle() | 3344 | and headers were completely prepared by single MHD_request_handle_idle_() |
1867 | then try not to wait for next sockets polling and send response | 3345 | then try not to wait for next sockets polling and send response |
1868 | immediately. | 3346 | immediately. |
1869 | As writeability of socket was not checked and it may have | 3347 | As writeability of socket was not checked and it may have |
@@ -1877,17 +3355,17 @@ MHD_connection_call_handlers_ (struct MHD_Connection *con, | |||
1877 | if (MHD_REQUEST_HEADERS_SENDING == con->request.state) | 3355 | if (MHD_REQUEST_HEADERS_SENDING == con->request.state) |
1878 | { | 3356 | { |
1879 | MHD_request_handle_write_ (&con->request); | 3357 | MHD_request_handle_write_ (&con->request); |
1880 | /* Always call 'MHD_connection_handle_idle()' after each read/write. */ | 3358 | /* Always call 'MHD_request_handle_idle_()' after each read/write. */ |
1881 | ret = MHD_connection_handle_idle (con); | 3359 | ret = MHD_request_handle_idle_ (&con->request); |
1882 | } | 3360 | } |
1883 | /* If all headers were sent by single write_handler() and | 3361 | /* If all headers were sent by single write_handler() and |
1884 | * response body is prepared by single MHD_connection_handle_idle() | 3362 | * response body is prepared by single MHD_request_handle_idle_() |
1885 | * call - continue. */ | 3363 | * call - continue. */ |
1886 | if ((MHD_REQUEST_NORMAL_BODY_READY == con->request.state) || | 3364 | if ((MHD_REQUEST_NORMAL_BODY_READY == con->request.state) || |
1887 | (MHD_REQUEST_CHUNKED_BODY_READY == con->request.state)) | 3365 | (MHD_REQUEST_CHUNKED_BODY_READY == con->request.state)) |
1888 | { | 3366 | { |
1889 | MHD_request_handle_write_ (&con->request); | 3367 | MHD_request_handle_write_ (&con->request); |
1890 | ret = MHD_connection_handle_idle (con); | 3368 | ret = MHD_request_handle_idle_ (&con->request); |
1891 | } | 3369 | } |
1892 | } | 3370 | } |
1893 | 3371 | ||
diff --git a/src/lib/connection_close.c b/src/lib/connection_close.c index 39045e9f..ad15ce7a 100644 --- a/src/lib/connection_close.c +++ b/src/lib/connection_close.c | |||
@@ -93,12 +93,10 @@ MHD_connection_close_ (struct MHD_Connection *connection, | |||
93 | connection->request.response = NULL; | 93 | connection->request.response = NULL; |
94 | MHD_response_queue_for_destroy (resp); | 94 | MHD_response_queue_for_destroy (resp); |
95 | } | 95 | } |
96 | if ( (NULL != daemon->notify_connection_cb) && | 96 | if (NULL != daemon->notify_connection_cb) |
97 | (connection->client_aware) ) | ||
98 | daemon->notify_connection_cb (daemon->notify_connection_cb_cls, | 97 | daemon->notify_connection_cb (daemon->notify_connection_cb_cls, |
99 | connection, | 98 | connection, |
100 | cnc); | 99 | cnc); |
101 | connection->client_aware = false; | ||
102 | } | 100 | } |
103 | 101 | ||
104 | /* end of connection_close.c */ | 102 | /* end of connection_close.c */ |
diff --git a/src/lib/internal.c b/src/lib/internal.c index ea7600c3..75eae8a4 100644 --- a/src/lib/internal.c +++ b/src/lib/internal.c | |||
@@ -178,11 +178,11 @@ MHD_http_unescape (char *val) | |||
178 | * clobbered in the process! | 178 | * clobbered in the process! |
179 | * @param cb function to call on each key-value pair found | 179 | * @param cb function to call on each key-value pair found |
180 | * @param[out] num_headers set to the number of headers found | 180 | * @param[out] num_headers set to the number of headers found |
181 | * @return #MHD_NO on failure (@a cb returned #MHD_NO), | 181 | * @return false on failure (@a cb returned false), |
182 | * #MHD_YES for success (parsing succeeded, @a cb always | 182 | * true for success (parsing succeeded, @a cb always |
183 | * returned #MHD_YES) | 183 | * returned true) |
184 | */ | 184 | */ |
185 | int | 185 | bool |
186 | MHD_parse_arguments_ (struct MHD_Request *request, | 186 | MHD_parse_arguments_ (struct MHD_Request *request, |
187 | enum MHD_ValueKind kind, | 187 | enum MHD_ValueKind kind, |
188 | char *args, | 188 | char *args, |
@@ -209,11 +209,11 @@ MHD_parse_arguments_ (struct MHD_Request *request, | |||
209 | daemon->unescape_cb (daemon->unescape_cb_cls, | 209 | daemon->unescape_cb (daemon->unescape_cb_cls, |
210 | request, | 210 | request, |
211 | args); | 211 | args); |
212 | if (MHD_YES != cb (request, | 212 | if (! cb (request, |
213 | args, | 213 | args, |
214 | NULL, | 214 | NULL, |
215 | kind)) | 215 | kind)) |
216 | return MHD_NO; | 216 | return false; |
217 | (*num_headers)++; | 217 | (*num_headers)++; |
218 | break; | 218 | break; |
219 | } | 219 | } |
@@ -228,11 +228,11 @@ MHD_parse_arguments_ (struct MHD_Request *request, | |||
228 | daemon->unescape_cb (daemon->unescape_cb_cls, | 228 | daemon->unescape_cb (daemon->unescape_cb_cls, |
229 | request, | 229 | request, |
230 | equals); | 230 | equals); |
231 | if (MHD_YES != cb (request, | 231 | if (! cb (request, |
232 | args, | 232 | args, |
233 | equals, | 233 | equals, |
234 | kind)) | 234 | kind)) |
235 | return MHD_NO; | 235 | return false; |
236 | (*num_headers)++; | 236 | (*num_headers)++; |
237 | break; | 237 | break; |
238 | } | 238 | } |
@@ -247,11 +247,11 @@ MHD_parse_arguments_ (struct MHD_Request *request, | |||
247 | daemon->unescape_cb (daemon->unescape_cb_cls, | 247 | daemon->unescape_cb (daemon->unescape_cb_cls, |
248 | request, | 248 | request, |
249 | args); | 249 | args); |
250 | if (MHD_YES != cb (request, | 250 | if (! cb (request, |
251 | args, | 251 | args, |
252 | NULL, | 252 | NULL, |
253 | kind)) | 253 | kind)) |
254 | return MHD_NO; | 254 | return false; |
255 | /* continue with 'bar' */ | 255 | /* continue with 'bar' */ |
256 | (*num_headers)++; | 256 | (*num_headers)++; |
257 | args = amper; | 257 | args = amper; |
@@ -269,15 +269,15 @@ MHD_parse_arguments_ (struct MHD_Request *request, | |||
269 | daemon->unescape_cb (daemon->unescape_cb_cls, | 269 | daemon->unescape_cb (daemon->unescape_cb_cls, |
270 | request, | 270 | request, |
271 | equals); | 271 | equals); |
272 | if (MHD_YES != cb (request, | 272 | if (! cb (request, |
273 | args, | 273 | args, |
274 | equals, | 274 | equals, |
275 | kind)) | 275 | kind)) |
276 | return MHD_NO; | 276 | return false; |
277 | (*num_headers)++; | 277 | (*num_headers)++; |
278 | args = amper; | 278 | args = amper; |
279 | } | 279 | } |
280 | return MHD_YES; | 280 | return true; |
281 | } | 281 | } |
282 | 282 | ||
283 | /* end of internal.c */ | 283 | /* end of internal.c */ |
diff --git a/src/lib/internal.h b/src/lib/internal.h index b4c471dd..c90fdd2d 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h | |||
@@ -795,14 +795,6 @@ struct MHD_Connection | |||
795 | bool suspended; | 795 | bool suspended; |
796 | 796 | ||
797 | /** | 797 | /** |
798 | * Did we ever call the "default_handler" on this request? (this | ||
799 | * flag will determine if we call the | ||
800 | * #MHD_daemon_set_notify_connection() handler when the connection | ||
801 | * closes down). | ||
802 | */ | ||
803 | bool client_aware; | ||
804 | |||
805 | /** | ||
806 | * Are we ready to read from TLS for this connection? | 798 | * Are we ready to read from TLS for this connection? |
807 | */ | 799 | */ |
808 | bool tls_read_ready; | 800 | bool tls_read_ready; |
@@ -1722,6 +1714,11 @@ struct MHD_Response | |||
1722 | * Only respond in HTTP 1.0 mode. | 1714 | * Only respond in HTTP 1.0 mode. |
1723 | */ | 1715 | */ |
1724 | bool v10_only; | 1716 | bool v10_only; |
1717 | |||
1718 | /** | ||
1719 | * Use ShoutCAST format. | ||
1720 | */ | ||
1721 | bool icy; | ||
1725 | 1722 | ||
1726 | }; | 1723 | }; |
1727 | 1724 | ||
@@ -1735,10 +1732,10 @@ struct MHD_Response | |||
1735 | * @param key 0-terminated key string, never NULL | 1732 | * @param key 0-terminated key string, never NULL |
1736 | * @param value 0-terminated value string, may be NULL | 1733 | * @param value 0-terminated value string, may be NULL |
1737 | * @param kind origin of the key-value pair | 1734 | * @param kind origin of the key-value pair |
1738 | * @return #MHD_YES on success (continue to iterate) | 1735 | * @return true on success (continue to iterate) |
1739 | * #MHD_NO to signal failure (and abort iteration) | 1736 | * false to signal failure (and abort iteration) |
1740 | */ | 1737 | */ |
1741 | typedef int | 1738 | typedef bool |
1742 | (*MHD_ArgumentIterator_)(struct MHD_Request *request, | 1739 | (*MHD_ArgumentIterator_)(struct MHD_Request *request, |
1743 | const char *key, | 1740 | const char *key, |
1744 | const char *value, | 1741 | const char *value, |
@@ -1755,11 +1752,11 @@ typedef int | |||
1755 | * clobbered in the process! | 1752 | * clobbered in the process! |
1756 | * @param cb function to call on each key-value pair found | 1753 | * @param cb function to call on each key-value pair found |
1757 | * @param[out] num_headers set to the number of headers found | 1754 | * @param[out] num_headers set to the number of headers found |
1758 | * @return #MHD_NO on failure (@a cb returned #MHD_NO), | 1755 | * @return false on failure (@a cb returned false), |
1759 | * #MHD_YES for success (parsing succeeded, @a cb always | 1756 | * true for success (parsing succeeded, @a cb always |
1760 | * returned #MHD_YES) | 1757 | * returned true) |
1761 | */ | 1758 | */ |
1762 | int | 1759 | bool |
1763 | MHD_parse_arguments_ (struct MHD_Request *request, | 1760 | MHD_parse_arguments_ (struct MHD_Request *request, |
1764 | enum MHD_ValueKind kind, | 1761 | enum MHD_ValueKind kind, |
1765 | char *args, | 1762 | char *args, |