/** * @brief Read a line of input from a network connection. * @note This function handles reading data from both regular and ssl connections. * This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received. * If a new line is read, the length of that line is returned to the caller, including the trailing \n. * If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1. * If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2. * Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1. * @param con the network connection across which the line of data will be read. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character. */ int64_t con_read_line(connection_t *con, bool_t block) { ssize_t bytes = 0; int_t counter = 0; bool_t line = false; if (!con || con->network.sockd == -1 || con_status(con) < 0) { if (con) con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. else if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check if we have received more data than just what is in the current line of input. else if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // If so, move the unused "new" data after the current line marker to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the buffer length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { con->network.status = 1; return pl_length_get(con->network.line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // blocking = st_length_get(con->network.buffer) ? false : true; block = true; if (con->network.tls) { bytes = tls_read(con->network.tls, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); } else { bytes = tcp_read(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); } // We actually read in data, so we need to update the buffer to reflect the amount of unprocessed data it currently holds. if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } else if (bytes == 0) { usleep(1000); } else { con->network.status = -1; return -1; } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { line = true; } } while (!line && block && counter++ < 128 && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer) && status()); if (st_length_get(con->network.buffer) > 0) { con->network.status = 1; } return pl_length_get(con->network.line); }
/** * @brief Read a line of input from a network connection. * @note This function handles reading data from both regular and ssl connections. * This function continually attempts to read incoming data from the specified connection until a \n terminated line of input is received. * If a new line is read, the length of that line is returned to the caller, including the trailing \n. * If the read returns -1 and wasn't caused by a syscall interruption or blocking error, -1 is returned, and the connection status is set to -1. * If the read returns 0 and wasn't caused by a syscall interruption or blocking error, -2 is returned, and the connection status is set to 2. * Once a \n character is reached, the length of the current line of input is returned to the user, and the connection status is set to 1. * @param con the network connection across which the line of data will be read. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing \n. */ int64_t con_read_line(connection_t *con, bool_t block) { ssize_t bytes; bool_t line = false; if (!con || con->network.sockd == -1) { con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check if we have received more data than just what is in the current line of input. if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // If so, move the unused "new" data after the current line marker to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the buffer length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { con->network.status = 1; return pl_length_get(con->network.line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (con->network.ssl) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = ssl_read(con->network.ssl, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), block); if (bytes <= 0 && bytes != SSL_ERROR_WANT_READ) { con->network.status = -1; return -1; } else if (bytes <= 0) { return 0; } } else { bytes = recv(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), (block ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { con->network.status = -1; return -1; } else if (!bytes) { con->network.status = 2; return -2; } } if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(con->network.buffer) && !pl_empty((con->network.line = line_pl_st(con->network.buffer, 0)))) { line = true; } } while (status() && !line && st_length_get(con->network.buffer) != st_avail_get(con->network.buffer)); if (st_length_get(con->network.buffer) > 0) { con->network.status = 1; } return pl_length_get(con->network.line); }
/** * @brief Read data from a network connection, and store the data in the connection context buffer. * @note This function handles reading data from both regular and SSL connections. * If the connection's network buffer hasn't been allocated, it will be initialized. * @param con a pointer to the connection object from which the data will be read. * @return -1 on general failure, -2 if the connection was reset, or the amount of data that was read. */ int64_t con_read(connection_t *con) { ssize_t bytes = 0; int_t counter = 0; bool_t blocking = true; if (!con || con->network.sockd == -1 || con_status(con) < 0) { if (con) con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. else if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check for data past the current line buffer. else if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Clear the line buffer. con->network.line = pl_null(); if (st_length_get(con->network.buffer)) { return st_length_get(con->network.buffer); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until the buffer has data or we get an error. do { // blocking = st_length_get(con->network.buffer) ? false : true; blocking = true; if (con->network.tls) { bytes = tls_read(con->network.tls, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); } else { bytes = tcp_read(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); } // We actually read in data, so we need to update the buffer to reflect the amount of unprocessed data it currently holds. if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); } else if (bytes == 0) { usleep(1000); } else { con->network.status = -1; return -1; } } while (blocking && counter++ < 128 && !st_length_get(con->network.buffer) && status()); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(con->network.buffer)) { con->network.status = 1; } return st_length_get(con->network.buffer); }
/** * @brief Read data from a connection, and store it in its internal buffer. * @note This function handles reading data from both regular and ssl connections. * If the connection's network buffer hasn't been allocated, it will be initialized. * @param con a pointer to the connection object from which the data will be read. * @return -1 on internal error, or on a read error. * * * */ int64_t con_read(connection_t *con) { ssize_t bytes; bool_t blocking; if (!con || con->network.sockd == -1) { con->network.status = -1; return -1; } // Check for an existing network buffer. If there isn't one, try creating it. if (!con->network.buffer && !con_init_network_buffer(con)) { con->network.status = -1; return -1; } // Check for data past the current line buffer. if (pl_length_get(con->network.line) && st_length_get(con->network.buffer) > pl_length_get(con->network.line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(con->network.buffer), st_data_get(con->network.buffer) + pl_length_get(con->network.line), st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Update the length. st_length_set(con->network.buffer, st_length_get(con->network.buffer) - pl_length_get(con->network.line)); // Clear the line buffer. con->network.line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(con->network.buffer, 0); con->network.line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(con->network.buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (con->network.ssl) { bytes = ssl_read(con->network.ssl, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking); if (!bytes && ssl_shutdown_get(con->network.ssl)) { con->network.status = -1; return -1; } } else { bytes = recv(con->network.sockd, st_char_get(con->network.buffer) + st_length_get(con->network.buffer), st_avail_get(con->network.buffer) - st_length_get(con->network.buffer), blocking ? 0 : MSG_DONTWAIT); if (bytes < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { con->network.status = -1; return -1; } } if (bytes > 0) { st_length_set(con->network.buffer, st_length_get(con->network.buffer) + bytes); // Or break out of the loop because we've been shutdown. } else if (!bytes) { break; } } while (status() && blocking && !st_length_get(con->network.buffer)); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(con->network.buffer)) { con->network.status = 1; } else if (!bytes) { con->network.status = 2; return -2; } return st_length_get(con->network.buffer); }