*/
int osrfUtilsCheckFileDescriptor( int fd );
+/*
+ Returns the approximate additional length of
+ a string after XML escaping <, >, &, and ".
+*/
+size_t osrfXmlEscapingLength ( const char* str );
+
#ifdef __cplusplus
}
#endif
OSRF_STATUS_COMPLETE );
if (data) {
-
char* json = jsonObjectToJSON(data);
- size_t data_size = strlen(json);
+ size_t raw_size = strlen(json);
+ size_t extra_size = osrfXmlEscapingLength(json);
+ size_t data_size = raw_size + extra_size;
size_t chunk_size = OSRF_MSG_CHUNK_SIZE;
- if (chunk_size > 0 && chunk_size < data_size) {
- osrfSendChunkedResult(ses, requestId, json, data_size, chunk_size);
+ if (data_size > chunk_size) // calculate an escape-scaled chunk size
+ chunk_size = ((double)raw_size / (double)data_size) * (double)chunk_size;
+
+ if (chunk_size > 0 && chunk_size < raw_size) {
+ // chunking -- response message exceeds max message size.
+ // break it up into chunks for partial delivery
+
+ osrfSendChunkedResult(ses, requestId, json, raw_size, chunk_size);
osrfAppSessionSendBatch( ses, &status, 1 );
} else {
if( data ) {
char* data_str = jsonObjectToJSON(data); // free me (below)
- size_t data_size = strlen(data_str);
+ size_t raw_size = strlen(data_str);
+ size_t extra_size = osrfXmlEscapingLength(data_str);
+ size_t data_size = raw_size + extra_size;
size_t chunk_size = ctx->method->max_chunk_size;
- if (chunk_size > 0 && chunk_size < data_size) {
+ if (data_size > chunk_size) // calculate an escape-scaled chunk size
+ chunk_size = ((double)raw_size / (double)data_size) * (double)chunk_size;
+
+ if (chunk_size > 0 && chunk_size < raw_size) {
// chunking -- response message exceeds max message size.
// break it up into chunks for partial delivery
osrfSendChunkedResult(ctx->session, ctx->request,
- data_str, data_size, chunk_size);
+ data_str, raw_size, chunk_size);
} else {
return 0;
}
+size_t osrfXmlEscapingLength ( const char* str ) {
+ int extra = 0;
+ const char* s;
+ for (s = str; *s; ++s) {
+ switch (*s) {
+ case '>':
+ case '<':
+ extra += 3;
+ break;
+ case '&':
+ extra += 4;
+ break;
+ case '"':
+ extra += 11;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return extra;
+}
+
if ($self->max_chunk_size > 0) { # we might need to chunk
my $str = OpenSRF::Utils::JSON->perl2JSON($msg);
- if (length($str) > $self->max_chunk_size) { # send partials ("chunking")
- for (my $i = 0; $i < length($str); $i += $self->max_chunk_size) {
+
+ # XML can add a lot of length to a chunk due to escaping, so we
+ # calculate chunk size based on an XML-escaped version of the message.
+ # Example: If escaping doubles the length of the string then $ratio
+ # will be 0.5 and we'll cut the chunk size for this message in half.
+
+ my $raw_length = length($str);
+ my $escaped_length = $raw_length;
+ $escaped_length += 11 * (() = ( $str =~ /"/g)); # 7 \s and "
+ $escaped_length += 4 * (() = ( $str =~ /&/g)); # &
+ $escaped_length += 3 * (() = ( $str =~ /[<>]/g)); # < / >
+
+ my $chunk_size = $self->max_chunk_size;
+
+ if ($escaped_length > $self->max_chunk_size) {
+ $chunk_size = ($raw_length / $escaped_length) * $self->max_chunk_size;
+ }
+
+ if ($raw_length > $chunk_size) { # send partials ("chunking")
+ for (my $i = 0; $i < length($str); $i += $chunk_size) {
$response = new OpenSRF::DomainObject::oilsResult::Partial;
- $response->content( substr($str, $i, $self->max_chunk_size) );
+ $response->content( substr($str, $i, $chunk_size) );
$self->session->send($type, $response, $self->threadTrace);
}
# This triggers reconstruction on the remote end