Back to index

lightning-sunbird  0.9+nobinonly
nsHttpChunkedDecoder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsHttpChunkedDecoder.h"
00040 #include "nsHttp.h"
00041 
00042 //-----------------------------------------------------------------------------
00043 // nsHttpChunkedDecoder <public>
00044 //-----------------------------------------------------------------------------
00045 
00046 nsresult
00047 nsHttpChunkedDecoder::HandleChunkedContent(char *buf,
00048                                            PRUint32 count,
00049                                            PRUint32 *contentRead,
00050                                            PRUint32 *contentRemaining)
00051 {
00052     LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count));
00053 
00054     *contentRead = 0;
00055     
00056     // from RFC2617 section 3.6.1, the chunked transfer coding is defined as:
00057     //
00058     //   Chunked-Body    = *chunk
00059     //                     last-chunk
00060     //                     trailer
00061     //                     CRLF
00062     //   chunk           = chunk-size [ chunk-extension ] CRLF
00063     //                     chunk-data CRLF
00064     //   chunk-size      = 1*HEX
00065     //   last-chunk      = 1*("0") [ chunk-extension ] CRLF
00066     //       
00067     //   chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
00068     //   chunk-ext-name  = token
00069     //   chunk-ext-val   = token | quoted-string
00070     //   chunk-data      = chunk-size(OCTET)
00071     //   trailer         = *(entity-header CRLF)
00072     //
00073     // the chunk-size field is a string of hex digits indicating the size of the
00074     // chunk.  the chunked encoding is ended by any chunk whose size is zero, 
00075     // followed by the trailer, which is terminated by an empty line.
00076 
00077     while (count) {
00078         if (mChunkRemaining) {
00079             PRUint32 amt = PR_MIN(mChunkRemaining, count);
00080 
00081             count -= amt;
00082             mChunkRemaining -= amt;
00083 
00084             *contentRead += amt;
00085             buf += amt;
00086         }
00087         else if (mReachedEOF)
00088             break; // done
00089         else {
00090             PRUint32 bytesConsumed = 0;
00091 
00092             nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed);
00093             if (NS_FAILED(rv)) return rv;
00094 
00095             count -= bytesConsumed;
00096 
00097             if (count) {
00098                 // shift buf by bytesConsumed
00099                 memmove(buf, buf + bytesConsumed, count);
00100             }
00101         }
00102     }
00103     
00104     *contentRemaining = count;
00105     return NS_OK;
00106 }
00107 
00108 //-----------------------------------------------------------------------------
00109 // nsHttpChunkedDecoder <private>
00110 //-----------------------------------------------------------------------------
00111 
00112 nsresult
00113 nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
00114                                           PRUint32 count,
00115                                           PRUint32 *bytesConsumed)
00116 {
00117     NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero");
00118     NS_PRECONDITION(count, "unexpected");
00119 
00120     *bytesConsumed = 0;
00121     
00122     char *p = NS_STATIC_CAST(char *, memchr(buf, '\n', count));
00123     if (p) {
00124         *p = 0;
00125         if ((p > buf) && (*(p-1) == '\r')) // eliminate a preceding CR
00126             *(p-1) = 0;
00127         *bytesConsumed = p - buf + 1;
00128 
00129         // make buf point to the full line buffer to parse
00130         if (!mLineBuf.IsEmpty()) {
00131             mLineBuf.Append(buf);
00132             buf = (char *) mLineBuf.get();
00133         }
00134 
00135         if (mWaitEOF) {
00136             if (*buf) {
00137                 LOG(("got trailer: %s\n", buf));
00138                 // allocate a header array for the trailers on demand
00139                 if (!mTrailers) {
00140                     mTrailers = new nsHttpHeaderArray();
00141                     if (!mTrailers)
00142                         return NS_ERROR_OUT_OF_MEMORY;
00143                 }
00144                 mTrailers->ParseHeaderLine(buf);
00145             }
00146             else {
00147                 mWaitEOF = PR_FALSE;
00148                 mReachedEOF = PR_TRUE;
00149                 LOG(("reached end of chunked-body\n"));
00150             }
00151         }
00152         else if (*buf) {
00153             // ignore any chunk-extensions
00154             if ((p = PL_strchr(buf, ';')) != nsnull)
00155                 *p = 0;
00156 
00157             if (!sscanf(buf, "%x", &mChunkRemaining)) {
00158                 LOG(("sscanf failed parsing hex on string [%s]\n", buf));
00159                 return NS_ERROR_UNEXPECTED;
00160             }
00161 
00162             // we've discovered the last chunk
00163             if (mChunkRemaining == 0)
00164                 mWaitEOF = PR_TRUE;
00165         }
00166 
00167         // ensure that the line buffer is clear
00168         mLineBuf.Truncate();
00169     }
00170     else {
00171         // save the partial line; wait for more data
00172         *bytesConsumed = count;
00173         // ignore a trailing CR
00174         if (buf[count-1] == '\r')
00175             count--;
00176         mLineBuf.Append(buf, count);
00177     }
00178 
00179     return NS_OK;
00180 }