| /* |
| * 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 <axutil_http_chunked_stream.h> |
| #include <axutil_string.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #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_ENV_CHECK(env, 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_ENV_CHECK(env, void); |
| |
| AXIS2_FREE(env->allocator, chunked_stream); |
| return; |
| } |
| |
| 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); |
| len = 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; |
| } |
| |