1 /* ------------------------------------------------------------------
2 * Copyright (C) 1998-2009 PacketVideo
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13 * express or implied.
14 * See the License for the specific language governing permissions
15 * and limitations under the License.
16 * -------------------------------------------------------------------
17 */
18 #include "http_composer.h"
19 #include "http_parcom_internal.h"
20 #include "string_keyvalue_store.h"
21 #include "oscl_string_utils.h"
22 #include "oscl_string_containers.h"
23
24
25 ////////////////////////////////////////////////////////////////////////////////////
26 ////// HTTPComposer implementation /////////////////////////////////////////////////
27 ////////////////////////////////////////////////////////////////////////////////////
28
reset(const bool aKeepAllSettingsExceptURI)29 OSCL_EXPORT_REF void HTTPComposer::reset(const bool aKeepAllSettingsExceptURI)
30 {
31 // reset URI
32 iURI.setPtrLen("", 0);
33 iHeaderLength = 0;
34 iFirstLineLength = 0;
35 iEntityBodyLength = 0;
36 if (aKeepAllSettingsExceptURI) return;
37
38 // reset other stuff
39 iMethod = HTTP_METHOD_GET;
40 iVersion = HTTP_V1_0;
41
42 if (iKeyValueStore) iKeyValueStore->clear();
43 }
44
45 ////////////////////////////////////////////////////////////////////////////////////
46 // factory method
create()47 OSCL_EXPORT_REF HTTPComposer* HTTPComposer::create()
48 {
49 HTTPComposer *composer = OSCL_NEW(HTTPComposer, ());
50 if (!composer) return NULL;
51 if (!composer->construct())
52 {
53 OSCL_DELETE(composer);
54 return NULL;
55 }
56 return composer;
57 }
58
59 ////////////////////////////////////////////////////////////////////////////////////
construct()60 bool HTTPComposer::construct()
61 {
62 reset();
63 if ((iKeyValueStore = StringKeyValueStore::create()) == NULL) return false;
64 return true;
65 }
66
67 ////////////////////////////////////////////////////////////////////////////////////
68 // destructor
~HTTPComposer()69 OSCL_EXPORT_REF HTTPComposer::~HTTPComposer()
70 {
71 reset();
72
73 // delete iKeyValueStore
74 if (iKeyValueStore) OSCL_DELETE(iKeyValueStore);
75 iKeyValueStore = NULL;
76 }
77
78 ////////////////////////////////////////////////////////////////////////////////////
setURI(const StrPtrLen aURI)79 OSCL_EXPORT_REF void HTTPComposer::setURI(const StrPtrLen aURI)
80 {
81 iURI = aURI;
82
83 // get relative URI
84 const char *aServerAddPtr = oscl_strstr(((char*)aURI.c_str()), "//");
85 if (!aServerAddPtr) return;
86 aServerAddPtr += 2;
87 const char *aRelativeUriPtr = oscl_strstr(aServerAddPtr, "/");
88 if (!aRelativeUriPtr) return;
89 iRelativeURI = StrPtrLen(aRelativeUriPtr);
90 }
91
92 ////////////////////////////////////////////////////////////////////////////////////
setField(const StrCSumPtrLen & aNewFieldName,const char * aNewFieldValue,const bool aNeedReplaceOldValue)93 OSCL_EXPORT_REF bool HTTPComposer::setField(const StrCSumPtrLen &aNewFieldName, const char *aNewFieldValue, const bool aNeedReplaceOldValue)
94 {
95 if (!iKeyValueStore) return false;
96
97 if (aNewFieldValue)
98 {
99 return (iKeyValueStore->addKeyValuePair(aNewFieldName, aNewFieldValue, aNeedReplaceOldValue) ==
100 StringKeyValueStore::StringKeyValueStore_Success);
101 }
102
103 // remove this field
104 return iKeyValueStore->removeKeyValuePair(aNewFieldName);
105 }
106
107 ////////////////////////////////////////////////////////////////////////////////////
setField(const StrCSumPtrLen & aNewFieldName,const StrPtrLen * aNewFieldValue,const bool aNeedReplaceOldValue)108 OSCL_EXPORT_REF bool HTTPComposer::setField(const StrCSumPtrLen &aNewFieldName, const StrPtrLen *aNewFieldValue, const bool aNeedReplaceOldValue)
109 {
110 if (!iKeyValueStore) return false;
111 if (!aNewFieldValue) return iKeyValueStore->removeKeyValuePair(aNewFieldName);
112 return (iKeyValueStore->addKeyValuePair(aNewFieldName, *aNewFieldValue, aNeedReplaceOldValue) ==
113 StringKeyValueStore::StringKeyValueStore_Success);
114 }
115
116
117 ////////////////////////////////////////////////////////////////////////////////////
getCurrentRequestLength(const bool usingAbsoluteURI)118 OSCL_EXPORT_REF int32 HTTPComposer::getCurrentRequestLength(const bool usingAbsoluteURI)
119 {
120 // sanity check
121 if (!usingAbsoluteURI && iRelativeURI.length() == 0) return COMPOSE_BAD_URI;
122
123 // since iHeaderLength is updated for each setField, we only need to calculate the request-line length
124 // Request-line: Method SP Request-URI SP HTTP-Version CRLF
125 iFirstLineLength = oscl_strlen(HTTPMethodString[(uint32)iMethod]); // method
126 StrPtrLen *uriPtr = (usingAbsoluteURI ? (&iURI) : (&iRelativeURI));
127 iFirstLineLength += uriPtr->length(); // uri
128 iFirstLineLength += 8; // "HTTP/1.1" or "HTTP/1.0"
129 iFirstLineLength += 4; // 2 SPs + CRLF
130 iHeaderLength = iFirstLineLength;
131
132 // header fields part
133 iHeaderLength += (iKeyValueStore->getTotalKeyValueLength() +
134 iKeyValueStore->getNumberOfKeyValuePairs() * 4); // 4 => key:SPvalueCRLF, 1SP + ':' + CRLT
135
136 iHeaderLength += 2; // final CRLF
137
138 return (int32)(iHeaderLength + iEntityBodyLength);
139 }
140
141
142 ////////////////////////////////////////////////////////////////////////////////////
compose(OsclMemoryFragment & aComposedMessageBuffer,const bool usingAbsoluteURI,const uint32 aEntityBodyLength)143 OSCL_EXPORT_REF int32 HTTPComposer::compose(OsclMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI, const uint32 aEntityBodyLength)
144 {
145 HTTPMemoryFragment messageBuffer(aComposedMessageBuffer);
146
147 // sanity check, all the list error codes will pop up here if there is an error
148 int32 status = santityCheckForCompose(messageBuffer, usingAbsoluteURI, aEntityBodyLength);
149 if (status != COMPOSE_SUCCESS) return status;
150 iEntityBodyLength = aEntityBodyLength;
151
152 // compose the first request/status line
153 composeFirstLine(messageBuffer, usingAbsoluteURI);
154
155 // compose the header part: general header + request header + entity header
156 composeHeaders(messageBuffer);
157
158 // if there is an enity body, then it should be in place,
159 // then add NULL terminator for a string
160 if (messageBuffer.getAvailableSpace() > 0)
161 {
162 *((char*)messageBuffer.getPtr() + iEntityBodyLength) = HTTP_CHAR_NULL;
163 messageBuffer.update(1);
164 }
165
166 return COMPOSE_SUCCESS;
167 }
168
169 ////////////////////////////////////////////////////////////////////////////////////
170 // supporting function for compose()
santityCheckForCompose(HTTPMemoryFragment & aComposedMessageBuffer,const bool usingAbsoluteURI,const uint32 aEntityBodyLength)171 int32 HTTPComposer::santityCheckForCompose(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI, const uint32 aEntityBodyLength)
172 {
173 // check URI
174 if (!usingAbsoluteURI && iRelativeURI.length() == 0) return COMPOSE_BAD_URI;
175
176 // check input buffer size
177 if (!aComposedMessageBuffer.isSpaceEnough(getCurrentRequestLength(usingAbsoluteURI) + aEntityBodyLength))
178 {
179 return COMPOSE_BUFFER_TOO_SMALL;
180 }
181
182 // check URI
183 if (iURI.length() == 0) return COMPOSE_URI_NOT_SET;
184
185 // check content type and content length for entity body
186 if (aEntityBodyLength)
187 {
188 StrCSumPtrLen contentType("Content-Type");
189 if (!iKeyValueStore->isKeyValueAvailable(contentType)) // no "Content-Type" key
190 return COMPOSE_CONTENT_TYPE_NOT_SET_FOR_ENTITY_BODY;
191
192 StrCSumPtrLen contentLengthKey("Content-Length");
193 StrPtrLen contentLengthValue;
194 if (!iKeyValueStore->getValueByKey(contentLengthKey, contentLengthValue)) // no "Content-Length" key
195 return COMPOSE_CONTENT_LENGTH_NOT_SET_FOR_ENTITY_BODY;
196
197 uint32 contentLength;
198 PV_atoi(contentLengthValue.c_str(), 'd', contentLengthValue.length(), contentLength);
199 if (contentLength != aEntityBodyLength) return COMPOSE_CONTENT_LENGTH_NOT_MATCH_ENTITY_BODY_LENGTH;
200 }
201
202 return COMPOSE_SUCCESS;
203 }
204
205 ////////////////////////////////////////////////////////////////////////////////////
composeFirstLine(HTTPMemoryFragment & aComposedMessageBuffer,const bool usingAbsoluteURI)206 void HTTPComposer::composeFirstLine(HTTPMemoryFragment &aComposedMessageBuffer, const bool usingAbsoluteURI)
207 {
208 // Request-line: Method SP Request-URI SP HTTP-Version CRLF
209 char *ptr = (char *)aComposedMessageBuffer.getPtr();
210 oscl_memcpy(ptr, HTTPMethodString[(uint32)iMethod], oscl_strlen(HTTPMethodString[(uint32)iMethod]));
211 ptr += oscl_strlen(HTTPMethodString[iMethod]);
212 *ptr++ = HTTP_CHAR_SPACE;
213 StrPtrLen *uriPtr = (usingAbsoluteURI ? (&iURI) : (&iRelativeURI));
214 oscl_memcpy(ptr, uriPtr->c_str(), uriPtr->length());
215 ptr += uriPtr->length();
216 *ptr++ = HTTP_CHAR_SPACE;
217
218 // HTTP version
219 OSCL_FastString versionString;
220 if (iVersion == HTTP_V1_1)
221 {
222 versionString.set((OSCL_String::chartype*)_STRLIT_CHAR("HTTP/1.1"), 8);
223 }
224 else
225 {
226 versionString.set((OSCL_String::chartype*)_STRLIT_CHAR("HTTP/1.0"), 8);
227 }
228 oscl_memcpy(ptr, versionString.get_cstr(), 8);
229 ptr += 8;
230 *ptr++ = HTTP_CHAR_CR;
231 *ptr++ = HTTP_CHAR_LF;
232
233 OSCL_ASSERT(iFirstLineLength == (uint32)(ptr - (char*)aComposedMessageBuffer.getPtr()));
234 aComposedMessageBuffer.update(iFirstLineLength);
235 }
236
237 ////////////////////////////////////////////////////////////////////////////////////
238 class CleanupObject
239 {
240 StrPtrLen *iKeyList;
241 StrPtrLen *iValueList;
242
243 public:
CleanupObject(StrPtrLen * aKeyList=NULL,StrPtrLen * aValueList=NULL)244 CleanupObject(StrPtrLen *aKeyList = NULL, StrPtrLen *aValueList = NULL) :
245 iKeyList(aKeyList), iValueList(aValueList)
246 {
247 ;
248 }
249
250 //! Use destructor to do all the clean up work
~CleanupObject()251 ~CleanupObject()
252 {
253 if (iKeyList) OSCL_ARRAY_DELETE(iKeyList);
254 if (iValueList) OSCL_ARRAY_DELETE(iValueList);
255 }
256
setKeyList(StrPtrLen * aKeyList)257 void setKeyList(StrPtrLen *aKeyList)
258 {
259 iKeyList = aKeyList;
260 }
setValueList(StrPtrLen * aValueList)261 void setValueList(StrPtrLen *aValueList)
262 {
263 iValueList = aValueList;
264 }
265 };
266
composeHeaders(HTTPMemoryFragment & aComposedMessageBuffer)267 bool HTTPComposer::composeHeaders(HTTPMemoryFragment &aComposedMessageBuffer)
268 {
269 char *ptr = (char *)aComposedMessageBuffer.getPtr();
270 CleanupObject cleanup;
271
272 uint32 numKeyValuePairs = iKeyValueStore->getNumberOfKeyValuePairs();
273 if (numKeyValuePairs > 0)
274 {
275 StrPtrLen *keyList = OSCL_ARRAY_NEW(StrPtrLen, numKeyValuePairs);
276 cleanup.setKeyList(keyList);
277 StrPtrLen *valueList = OSCL_ARRAY_NEW(StrPtrLen, numKeyValuePairs);
278 cleanup.setValueList(valueList);
279 if (!keyList || !valueList) return false; // let cleanup object to do automatic deallocation
280
281 // get key list
282 uint32 numKeys = iKeyValueStore->getCurrentKeyList(keyList);
283 if (numKeys == 0) return false;
284
285 uint32 i, j, index = 0;
286 for (i = 0, j = 0; i < numKeyValuePairs && j < numKeys; j++)
287 {
288 index = 0;
289 while (iKeyValueStore->getValueByKey(keyList[j], valueList[i], index))
290 {
291 // put key
292 oscl_memcpy(ptr, keyList[j].c_str(), keyList[j].length()); // same field key
293 ptr += keyList[j].length();
294 *ptr++ = HTTP_CHAR_COLON;
295 *ptr++ = HTTP_CHAR_SPACE;
296
297 // put value
298 oscl_memcpy(ptr, valueList[i].c_str(), valueList[i].length()); // same field key
299 ptr += valueList[i].length();
300 *ptr++ = HTTP_CHAR_CR;
301 *ptr++ = HTTP_CHAR_LF;
302 index++;
303 }
304 i += index;
305 }
306 }
307
308 // add final CRLF
309 *ptr++ = HTTP_CHAR_CR;
310 *ptr++ = HTTP_CHAR_LF;
311
312 OSCL_ASSERT(iHeaderLength == (ptr - (char*)aComposedMessageBuffer.getPtr() + aComposedMessageBuffer.getLen()));
313 aComposedMessageBuffer.update(ptr);
314 return true;
315 }
316
317