PengZheng commented on code in PR #851:
URL: https://github.com/apache/celix/pull/851#discussion_r4309761703


##########
bundles/http_admin/http_admin/src/http_admin.c:
##########
@@ -173,6 +183,39 @@ void http_admin_removeHttpService(void *handle, void *svc 
CELIX_UNUSED, const ce
     }
 }
 
+static int httpAdmin_readRequestBody(struct mg_connection* connection, long 
long contentLength, char** bufferOut, size_t* bytesReadOut, const char** 
errorMsgOut) {
+    celix_autofree char* buffer = NULL;
+    size_t totalBytesRead = 0;
+    *errorMsgOut = NULL;
+    if (contentLength > 0) {
+        size_t length = (contentLength > INT_MAX) ? INT_MAX : 
(size_t)contentLength;
+        buffer = malloc(length + 1);

Review Comment:
   Shall we apply some upper bound to request body size? 
   For a request without explicit content-length (Transfer-Encoding: chunked), 
it is not safe to allocate so much memory.
   And it would be nice to have test cases for both such unusually large 
requests and reqeusts with chunked transfer-encoding.
   
   Or we may explicitly refuse requests without content-length by returning 
`411 (Length Required)`. 
   This way we cannot process chunked requests.



##########
bundles/http_admin/http_admin/src/http_admin.c:
##########
@@ -173,6 +183,39 @@ void http_admin_removeHttpService(void *handle, void *svc 
CELIX_UNUSED, const ce
     }
 }
 
+static int httpAdmin_readRequestBody(struct mg_connection* connection, long 
long contentLength, char** bufferOut, size_t* bytesReadOut, const char** 
errorMsgOut) {
+    celix_autofree char* buffer = NULL;
+    size_t totalBytesRead = 0;
+    *errorMsgOut = NULL;
+    if (contentLength > 0) {
+        size_t length = (contentLength > INT_MAX) ? INT_MAX : 
(size_t)contentLength;
+        buffer = malloc(length + 1);
+        if (buffer == NULL) {
+            *errorMsgOut = CELIX_HTTP_ADMIN_INSUFFICIENT_MEM_MSG;
+            return 500;
+        }
+
+        while (totalBytesRead < length) {
+            int read = mg_read(connection, buffer + totalBytesRead, length - 
totalBytesRead);
+            if (read == 0) {
+                break; //EOF
+            } else if (read < 0) {
+                *errorMsgOut = CELIX_HTTP_ADMIN_READ_ERROR_MSG;
+                return 400;
+            }
+            totalBytesRead += (size_t)read;
+        }
+        if (totalBytesRead < length) {

Review Comment:
   What if `contentLength == -1`? Then we can never receive a request without 
explicit `content_length` successfully.



##########
bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c:
##########
@@ -518,63 +529,100 @@ static int remoteServiceAdmin_callback(struct 
mg_connection *conn) {
             if (export != NULL) {
                 exportRegistration_increaseUsage(export);
             } else {
-                result = 0;
                 RSA_LOG_WARNING(rsa, "No export registration found for service 
id %lu", serviceId);
+                return 0;
             }
-            celixThreadRwlock_unlock(&rsa->exportedServicesLock);
         }
+    } else {
+        //no rsa call, let civetweb handle.
+        return 0;
+    }
+    assert(export != NULL);
 
+    long long contentLength = request_info->content_length; //-1 if no content 
length was provided
+    size_t maxRequestBodySize = rsa->maxRequestBodySize;
+    size_t readLen = 0;
 
-        if (export != NULL) {
-            uint64_t datalength = request_info->content_length;
-            char* data = malloc(datalength + 1);
-            mg_read(conn, data, datalength);
-            data[datalength] = '\0';
-
-            char *response = NULL;
-            int responceLength = 0;
-            int rc = exportRegistration_call(export, data, -1, &metadata, 
&response, &responceLength);
-            if (rc != CELIX_SUCCESS) {
-                RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got 
error %i\n", rc);
-            }
+    if (contentLength > 0 && (unsigned long long)contentLength > 
maxRequestBodySize) {
+        RSA_LOG_ERROR(rsa, "Error while reading request body, the body is 
larger than the maximum of %zu bytes", maxRequestBodySize);
+        mg_send_http_error(conn, 413, "%s", "Request body too large");

Review Comment:
   As already mentioned, `mg_send_http_error` will add status text 
corresponding to error code, as in 
https://github.com/civetweb/civetweb/blob/588860e30721bf5453b0440c390865a8e85dcae5/src/civetweb.c#L4677
   
   This remark applies to all usages of this API.



##########
bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c:
##########
@@ -518,63 +529,100 @@ static int remoteServiceAdmin_callback(struct 
mg_connection *conn) {
             if (export != NULL) {
                 exportRegistration_increaseUsage(export);
             } else {
-                result = 0;
                 RSA_LOG_WARNING(rsa, "No export registration found for service 
id %lu", serviceId);
+                return 0;
             }
-            celixThreadRwlock_unlock(&rsa->exportedServicesLock);
         }
+    } else {
+        //no rsa call, let civetweb handle.
+        return 0;
+    }
+    assert(export != NULL);
 
+    long long contentLength = request_info->content_length; //-1 if no content 
length was provided
+    size_t maxRequestBodySize = rsa->maxRequestBodySize;
+    size_t readLen = 0;
 
-        if (export != NULL) {
-            uint64_t datalength = request_info->content_length;
-            char* data = malloc(datalength + 1);
-            mg_read(conn, data, datalength);
-            data[datalength] = '\0';
-
-            char *response = NULL;
-            int responceLength = 0;
-            int rc = exportRegistration_call(export, data, -1, &metadata, 
&response, &responceLength);
-            if (rc != CELIX_SUCCESS) {
-                RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got 
error %i\n", rc);
-            }
+    if (contentLength > 0 && (unsigned long long)contentLength > 
maxRequestBodySize) {
+        RSA_LOG_ERROR(rsa, "Error while reading request body, the body is 
larger than the maximum of %zu bytes", maxRequestBodySize);
+        mg_send_http_error(conn, 413, "%s", "Request body too large");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
+     
+    size_t length = (contentLength > 0) ? (size_t)contentLength : 
(maxRequestBodySize+1) /*note +1 byte, to check if too body size goes over the 
limit*/;
+    celix_autofree char* data = malloc(length + 1);
+    if (data == NULL) {
+        RSA_LOG_ERROR(rsa, "Error while allocating a buffer of %zu bytes for 
the request body", (contentLength > 0) ? (size_t)contentLength : 
maxRequestBodySize);
+        mg_send_http_error(conn, 500, "%s", "Internal server error");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
 
-            if (rc == CELIX_SUCCESS && response != NULL) {
-                mg_write(conn, data_response_headers, 
strlen(data_response_headers));
+    while (readLen < length) {
+        int this_read = mg_read(conn, data + readLen, length - readLen);
+        if (this_read == 0) {
+            break; //EOF
+        } else if (this_read < 0) {
+            RSA_LOG_ERROR(rsa, "Error while reading request body, received %zu 
bytes", readLen);
+            mg_send_http_error(conn, 400, "%s", "Bad request");
+            exportRegistration_decreaseUsage(export);
+            return 1;
+        }
+        readLen += (size_t)this_read;
+    }
 
-                char *bufLoc = response;
-                size_t bytesLeft = strlen(response);
-                if (bytesLeft > INT_MAX) {
-                    //NOTE arcording to civetweb mg_write, there is a limit on 
mg_write for INT_MAX.
-                    RSA_LOG_WARNING(rsa, "nr of bytes to send for a remote 
call is > INT_MAX, this can lead to issues\n");
-                }
-                while (bytesLeft > 0) {
-                    int send = mg_write(conn, bufLoc, strlen(bufLoc));
-                    if (send > 0) {
-                        bytesLeft -= send;
-                        bufLoc += send;
-                    } else {
-                        RSA_LOG_ERROR(rsa, "Error sending response: %s", 
strerror(errno));
-                        break;
-                    }
-                }
+    if (contentLength <= 0 && readLen > maxRequestBodySize) {

Review Comment:
   `contentLength == 0` is invalid for our use case.



##########
bundles/shell/remote_shell/src/remote_shell.c:
##########
@@ -151,48 +168,65 @@ celix_status_t 
remoteShell_stopConnections(remote_shell_pt instance) {
 void *remoteShell_connection_run(void *data) {
        celix_status_t status = CELIX_SUCCESS;
        connection_pt connection = data;
-       size_t len;
+       ssize_t len;
        int result;
        struct timeval timeout; /* Timeout for select */
 
+       char buff[COMMAND_BUFF_SIZE];
+       size_t used = 0;
+
        int fd = fileno(connection->socketStream);
 
-       connection->threadRunning = true;
+       connection_setThreadRunning(connection, true);
        status = remoteShell_connection_print(connection, RS_WELCOME);
 
-       while (status == CELIX_SUCCESS && connection->threadRunning == true) {
+       while (status == CELIX_SUCCESS && 
connection_isThreadRunning(connection)) {
                do {
                        timeout.tv_sec = CONNECTION_LISTENER_TIMEOUT_SEC;
                        timeout.tv_usec = 0;
 
                        FD_ZERO(&connection->pollset);
                        FD_SET(fd, &connection->pollset);
                        result = select(fd + 1, &connection->pollset, NULL, 
NULL, &timeout);
-               } while (result == -1 && errno == EINTR && 
connection->threadRunning == true);
+               } while (result == -1 && errno == EINTR && 
connection_isThreadRunning(connection));
 
                /* The socket_fd has data available to be read */
                if (result > 0 && FD_ISSET(fd, &connection->pollset)) {

Review Comment:
   What if `result == -1` with a non-trivial errno? Should we quit?



##########
bundles/shell/remote_shell/src/remote_shell.c:
##########
@@ -140,7 +157,7 @@ celix_status_t remoteShell_stopConnections(remote_shell_pt 
instance) {
 
        for (i = 0; i < length; i += 1) {
                connection_pt connection = 
celix_arrayList_get(instance->connections, i);
-               connection->threadRunning = false;
+               connection_setThreadRunning(connection, false);

Review Comment:
   I have found no synchronization between connection processing thread and the 
calling thread, and thus suspect use-after-free bugs here.
   Moreover, we never calling pthread_join on these connection processing 
thread, these are definitely resource leaks.
   



##########
bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c:
##########
@@ -518,63 +529,100 @@ static int remoteServiceAdmin_callback(struct 
mg_connection *conn) {
             if (export != NULL) {
                 exportRegistration_increaseUsage(export);
             } else {
-                result = 0;
                 RSA_LOG_WARNING(rsa, "No export registration found for service 
id %lu", serviceId);
+                return 0;
             }
-            celixThreadRwlock_unlock(&rsa->exportedServicesLock);
         }
+    } else {
+        //no rsa call, let civetweb handle.
+        return 0;
+    }
+    assert(export != NULL);
 
+    long long contentLength = request_info->content_length; //-1 if no content 
length was provided
+    size_t maxRequestBodySize = rsa->maxRequestBodySize;
+    size_t readLen = 0;
 
-        if (export != NULL) {
-            uint64_t datalength = request_info->content_length;
-            char* data = malloc(datalength + 1);
-            mg_read(conn, data, datalength);
-            data[datalength] = '\0';
-
-            char *response = NULL;
-            int responceLength = 0;
-            int rc = exportRegistration_call(export, data, -1, &metadata, 
&response, &responceLength);
-            if (rc != CELIX_SUCCESS) {
-                RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got 
error %i\n", rc);
-            }
+    if (contentLength > 0 && (unsigned long long)contentLength > 
maxRequestBodySize) {
+        RSA_LOG_ERROR(rsa, "Error while reading request body, the body is 
larger than the maximum of %zu bytes", maxRequestBodySize);
+        mg_send_http_error(conn, 413, "%s", "Request body too large");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
+     
+    size_t length = (contentLength > 0) ? (size_t)contentLength : 
(maxRequestBodySize+1) /*note +1 byte, to check if too body size goes over the 
limit*/;
+    celix_autofree char* data = malloc(length + 1);
+    if (data == NULL) {
+        RSA_LOG_ERROR(rsa, "Error while allocating a buffer of %zu bytes for 
the request body", (contentLength > 0) ? (size_t)contentLength : 
maxRequestBodySize);
+        mg_send_http_error(conn, 500, "%s", "Internal server error");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
 
-            if (rc == CELIX_SUCCESS && response != NULL) {
-                mg_write(conn, data_response_headers, 
strlen(data_response_headers));
+    while (readLen < length) {
+        int this_read = mg_read(conn, data + readLen, length - readLen);
+        if (this_read == 0) {
+            break; //EOF
+        } else if (this_read < 0) {
+            RSA_LOG_ERROR(rsa, "Error while reading request body, received %zu 
bytes", readLen);
+            mg_send_http_error(conn, 400, "%s", "Bad request");
+            exportRegistration_decreaseUsage(export);
+            return 1;
+        }
+        readLen += (size_t)this_read;
+    }
 
-                char *bufLoc = response;
-                size_t bytesLeft = strlen(response);
-                if (bytesLeft > INT_MAX) {
-                    //NOTE arcording to civetweb mg_write, there is a limit on 
mg_write for INT_MAX.
-                    RSA_LOG_WARNING(rsa, "nr of bytes to send for a remote 
call is > INT_MAX, this can lead to issues\n");
-                }
-                while (bytesLeft > 0) {
-                    int send = mg_write(conn, bufLoc, strlen(bufLoc));
-                    if (send > 0) {
-                        bytesLeft -= send;
-                        bufLoc += send;
-                    } else {
-                        RSA_LOG_ERROR(rsa, "Error sending response: %s", 
strerror(errno));
-                        break;
-                    }
-                }
+    if (contentLength <= 0 && readLen > maxRequestBodySize) {
+        RSA_LOG_ERROR(rsa, "Error while reading request body, the body is 
larger than the maximum of %zu bytes", maxRequestBodySize);
+        mg_send_http_error(conn, 413, "%s", "Request body too large");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
 
-                free(response);
+    if (contentLength > 0 && readLen < (size_t)contentLength) {
+        RSA_LOG_ERROR(rsa, "Error while reading request body, received %zu of 
%llu bytes", readLen, (unsigned long long)contentLength);
+        mg_send_http_error(conn, 400, "%s", "Bad request");
+        exportRegistration_decreaseUsage(export);
+        return 1;
+    }
+    data[readLen] = '\0';
+
+    char *response = NULL;
+    int responceLength = 0;
+    int rc = exportRegistration_call(export, data, -1, &metadata, &response, 
&responceLength);
+    if (rc == CELIX_ILLEGAL_ARGUMENT) {
+        RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got error 
%i\n", rc);
+        mg_send_http_error(conn, 400, "%s", "Bad request");
+    } else if (rc != CELIX_SUCCESS) {
+        RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got error 
%i\n", rc);
+        mg_send_http_error(conn, 500, "%s", "Internal server error");
+    } else if (response != NULL) {
+        mg_write(conn, data_response_headers, strlen(data_response_headers));

Review Comment:
   It does not make sense to send `data_response_headers` and `response` 
differently.
   The return value of the former send is not checked at all.



##########
bundles/http_admin/http_admin/src/http_admin.c:
##########
@@ -31,6 +31,11 @@
 
 #include "celix_compiler.h"
 #include "celix_threads.h"
+#include "celix_stdlib_cleanup.h"
+
+#define CELIX_HTTP_ADMIN_READ_ERROR_MSG "Bad Request: Read error"
+#define CELIX_HTTP_ADMIN_INCOMPLETE_BODY_MSG "Bad Request: Incomplete Body"
+#define CELIX_HTTP_ADMIN_INSUFFICIENT_MEM_MSG "Server Error: Insufficient 
memory"

Review Comment:
   The status texts like "Bad Request"/"server Error" are redundent according 
to `mg_send_http_error` 
[documentation](https://github.com/civetweb/civetweb/blob/master/docs/api/mg_send_http_error.md):
   
   > The status text (e.g., "Not Found") for standard error codes is known by 
this function. A body of the error message, to explain the error in more 
detail, can be specified using the fmt format specifier and additional 
arguments. The fmt format specifier works like for the printf() function in the 
standard C library.
   
   Check 
https://github.com/civetweb/civetweb/blob/588860e30721bf5453b0440c390865a8e85dcae5/examples/rest/rest.c#L94
 for example usage.



##########
bundles/remote_services/remote_service_admin_dfi/src/remote_service_admin_dfi.c:
##########
@@ -518,63 +529,100 @@ static int remoteServiceAdmin_callback(struct 
mg_connection *conn) {
             if (export != NULL) {
                 exportRegistration_increaseUsage(export);
             } else {
-                result = 0;
                 RSA_LOG_WARNING(rsa, "No export registration found for service 
id %lu", serviceId);
+                return 0;
             }
-            celixThreadRwlock_unlock(&rsa->exportedServicesLock);
         }
+    } else {
+        //no rsa call, let civetweb handle.
+        return 0;
+    }
+    assert(export != NULL);
 
+    long long contentLength = request_info->content_length; //-1 if no content 
length was provided
+    size_t maxRequestBodySize = rsa->maxRequestBodySize;
+    size_t readLen = 0;
 
-        if (export != NULL) {
-            uint64_t datalength = request_info->content_length;
-            char* data = malloc(datalength + 1);
-            mg_read(conn, data, datalength);
-            data[datalength] = '\0';
-
-            char *response = NULL;
-            int responceLength = 0;
-            int rc = exportRegistration_call(export, data, -1, &metadata, 
&response, &responceLength);
-            if (rc != CELIX_SUCCESS) {
-                RSA_LOG_ERROR(rsa, "Error trying to invoke remove service, got 
error %i\n", rc);
-            }
+    if (contentLength > 0 && (unsigned long long)contentLength > 
maxRequestBodySize) {

Review Comment:
   Note that 0 is a valid `Content-Length` value, which just means message body 
is empty. 
   This is invalid for the remote service scenario, and thus we can avoid the 
following memory allocation and return error early.



##########
bundles/shell/remote_shell/src/remote_shell.c:
##########
@@ -172,27 +175,42 @@ void *remoteShell_connection_run(void *data) {
 
                /* The socket_fd has data available to be read */
                if (result > 0 && FD_ISSET(fd, &connection->pollset)) {
-                       char buff[COMMAND_BUFF_SIZE];
-
-                       len = recv(fd, buff, COMMAND_BUFF_SIZE - 1, 0);
-                       if (len < COMMAND_BUFF_SIZE) {
-                               celix_status_t commandStatus = CELIX_SUCCESS;
-                               buff[len] = '\0';
-
-                               commandStatus = 
remoteShell_connection_execute(connection, buff);
-
-                               if (commandStatus == CELIX_SUCCESS) {
-                                       
remoteShell_connection_print(connection, RS_PROMPT);
-                               } else if (commandStatus == 
CELIX_FILE_IO_EXCEPTION) {
-                                       //exit command
-                                       break;
-                               } else { //error
-                                       
remoteShell_connection_print(connection, RS_ERROR);
-                                       
remoteShell_connection_print(connection, RS_PROMPT);
+                       len = recv(fd, buff + used, COMMAND_BUFF_SIZE - 1 - 
used, 0);

Review Comment:
   This applies to sockets in non-blocking mode.
   I have not checked whether sockets in remote_shell has been set in this mode.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to