/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #define AXIS2_HTTP_CRLF "\r\n" struct axutil_http_chunked_stream { axutil_stream_t *stream; int current_chunk_size; int unread_len; axis2_bool_t end_of_chunks; axis2_bool_t chunk_started; }; static axis2_status_t axutil_http_chunked_stream_start_chunk( axutil_http_chunked_stream_t * chunked_stream, const axutil_env_t *env); AXIS2_EXTERN axutil_http_chunked_stream_t *AXIS2_CALL axutil_http_chunked_stream_create( const axutil_env_t *env, axutil_stream_t *stream) { axutil_http_chunked_stream_t *chunked_stream = NULL; AXIS2_PARAM_CHECK(env->error, stream, NULL); chunked_stream = (axutil_http_chunked_stream_t *)AXIS2_MALLOC(env->allocator, sizeof(axutil_http_chunked_stream_t)); if(!chunked_stream) { AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE); AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Out of memory"); return NULL; } chunked_stream->stream = stream; chunked_stream->current_chunk_size = -1; chunked_stream->unread_len = -1; chunked_stream->end_of_chunks = AXIS2_FALSE; chunked_stream->chunk_started = AXIS2_FALSE; return chunked_stream; } AXIS2_EXTERN void AXIS2_CALL axutil_http_chunked_stream_free( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env) { AXIS2_FREE(env->allocator, chunked_stream); } AXIS2_EXTERN int AXIS2_CALL axutil_http_chunked_stream_read( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env, void *buffer, size_t count) { int len = -1; int yet_to_read = 0; axutil_stream_t *stream = chunked_stream->stream; if(!buffer) { return -1; } if(!stream) { AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NULL_STREAM_IN_CHUNKED_STREAM, AXIS2_FAILURE); return -1; } if(AXIS2_TRUE == chunked_stream->end_of_chunks) { return 0; } if(AXIS2_FALSE == chunked_stream->chunk_started) { axutil_http_chunked_stream_start_chunk(chunked_stream, env); } yet_to_read = (int)count; /* We are sure that the difference lies within the int range */ while(AXIS2_FALSE == chunked_stream->end_of_chunks && yet_to_read > 0) { if(chunked_stream->unread_len < yet_to_read) { len = axutil_stream_read(chunked_stream->stream, env, (axis2_char_t *)buffer + count - yet_to_read, chunked_stream->unread_len); yet_to_read -= len; chunked_stream->unread_len -= len; if(chunked_stream->unread_len <= 0) { axutil_http_chunked_stream_start_chunk(chunked_stream, env); } } else { len = axutil_stream_read(chunked_stream->stream, env, (axis2_char_t *)buffer + count - yet_to_read, yet_to_read); yet_to_read -= len; chunked_stream->unread_len -= len; } } return ((int)count - yet_to_read); /* We are sure that the difference lies within the int range */ } AXIS2_EXTERN int AXIS2_CALL axutil_http_chunked_stream_write( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env, const void *buffer, size_t count) { axutil_stream_t *stream = chunked_stream->stream; int len = -1; axis2_char_t tmp_buf[10]; if(!buffer) { return -1; } if(!stream) { AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NULL_STREAM_IN_CHUNKED_STREAM, AXIS2_FAILURE); return -1; } sprintf(tmp_buf, "%x%s", (unsigned int)count, AXIS2_HTTP_CRLF); axutil_stream_write(stream, env, tmp_buf, axutil_strlen(tmp_buf)); len = axutil_stream_write(stream, env, buffer, count); axutil_stream_write(stream, env, AXIS2_HTTP_CRLF, 2); return len; } AXIS2_EXTERN int AXIS2_CALL axutil_http_chunked_stream_get_current_chunk_size( const axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env) { return chunked_stream->current_chunk_size; } static axis2_status_t axutil_http_chunked_stream_start_chunk( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env) { axis2_char_t tmp_buf[3] = ""; axis2_char_t str_chunk_len[512] = ""; axis2_char_t *tmp = NULL; int read = -1; /* remove the last CRLF of the previous chunk if any */ if(AXIS2_TRUE == chunked_stream->chunk_started) { read = axutil_stream_read(chunked_stream->stream, env, tmp_buf, 2); chunked_stream->chunk_started = AXIS2_FALSE; } /* read the len and chunk extension */ while((read = axutil_stream_read(chunked_stream->stream, env, tmp_buf, 1)) > 0) { tmp_buf[read] = '\0'; strcat(str_chunk_len, tmp_buf); if(0 != strstr(str_chunk_len, AXIS2_HTTP_CRLF)) { break; } } /* check whether we have extensions */ tmp = strchr(str_chunk_len, ';'); if(tmp) { /* we don't use extensions right now */ *tmp = '\0'; } chunked_stream->current_chunk_size = strtol(str_chunk_len, NULL, 16); if(0 == chunked_stream->current_chunk_size) { /* Read the last CRLF */ read = axutil_stream_read(chunked_stream->stream, env, tmp_buf, 2); chunked_stream->end_of_chunks = AXIS2_TRUE; } else { chunked_stream->chunk_started = AXIS2_TRUE; chunked_stream->unread_len = chunked_stream->current_chunk_size; } return AXIS2_SUCCESS; } AXIS2_EXTERN axis2_status_t AXIS2_CALL axutil_http_chunked_stream_write_last_chunk( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env) { axutil_stream_t *stream = NULL; stream = chunked_stream->stream; if(axutil_stream_write(stream, env, "0\r\n\r\n", 5) == 5) { return AXIS2_SUCCESS; } return AXIS2_FAILURE; } AXIS2_EXTERN axis2_bool_t AXIS2_CALL axutil_http_chunked_stream_get_end_of_chunks( axutil_http_chunked_stream_t *chunked_stream, const axutil_env_t *env) { return chunked_stream->end_of_chunks; }