00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <spl/Int32.h>
00021 #include <spl/web/HttpRequest.h>
00022 #include <spl/net/TcpSocket.h>
00023
00024 using namespace spl;
00025
00026 HttpRequest::HttpRequest()
00027 : m_method("GET"),
00028 m_uri(),
00029 m_httpVersion("HTTP/1.0"),
00030 m_header(),
00031 m_body(NULL),
00032 m_state(HTTPREQ_STATE_METHOD),
00033 m_accum(128)
00034 {
00035 }
00036
00037 HttpRequest::HttpRequest(const String& uri)
00038 : m_method("GET"),
00039 m_uri(uri),
00040 m_httpVersion("HTTP/1.0"),
00041 m_header(),
00042 m_body(NULL),
00043 m_state(HTTPREQ_STATE_METHOD),
00044 m_accum(128)
00045 {
00046 }
00047
00048 HttpRequest::HttpRequest(const HttpRequest& req)
00049 : m_method(req.m_method),
00050 m_uri(req.m_uri),
00051 m_httpVersion(req.m_httpVersion),
00052 m_header(req.m_header),
00053 m_body(NULL),
00054 m_state(req.m_state),
00055 m_accum(req.m_accum)
00056 {
00057 if ( NULL != req.m_body )
00058 {
00059 m_body = req.m_body->Clone();
00060 }
00061 }
00062
00063 HttpRequest::~HttpRequest()
00064 {
00065 if ( NULL != m_body )
00066 {
00067 delete m_body;
00068 }
00069 }
00070
00071 HttpRequest& HttpRequest::operator =(const HttpRequest& req)
00072 {
00073 m_method = req.m_method;
00074 m_uri = req.m_uri;
00075 m_httpVersion = req.m_httpVersion;
00076 m_header = req.m_header;
00077 m_state = req.m_state;
00078 m_accum = req.m_accum;
00079
00080 if ( NULL != m_body )
00081 {
00082 delete m_body;
00083 }
00084 if ( NULL == req.m_body )
00085 {
00086 m_body = NULL;
00087 }
00088 else
00089 {
00090 m_body = req.m_body->Clone();
00091 }
00092
00093 return *this;
00094 }
00095
00096 bool HttpRequest::ParseLine(const byte *data, int len, int *pos)
00097 {
00098 while ( *pos < len )
00099 {
00100 char ch = (char)data[(*pos)++];
00101 if ( ch == '\r' )
00102 {
00103 continue;
00104 }
00105 if ( ch == '\n' )
00106 {
00107 return true;
00108 }
00109 m_accum.Append( ch );
00110 }
00111 return false;
00112 }
00113
00114 void HttpRequest::Parse(const Array<byte>& data, int len)
00115 {
00116 int pos = 0;
00117
00118 while ( pos < len )
00119 {
00120 switch ( m_state )
00121 {
00122 case HTTPREQ_STATE_METHOD:
00123 while ( pos < len )
00124 {
00125 char ch = (char)data[pos++];
00126 if ( ch == ' ' )
00127 {
00128 m_method = *m_accum.ToString();
00129 m_accum.SetLength(0);
00130 m_state = HTTPREQ_STATE_URI;
00131 break;
00132 }
00133 m_accum.Append( ch );
00134 if ( m_accum.Length() > 6 )
00135 {
00136 throw new Exception(String::Format("%s %s", "Invalid HTTP method", m_accum.ToString()->GetChars())->GetChars());
00137 }
00138 }
00139 break;
00140
00141 case HTTPREQ_STATE_URI:
00142 while ( pos < len )
00143 {
00144 char ch = (char)data[pos++];
00145 if ( ch == ' ' )
00146 {
00147 StringPtr str = m_accum.ToString();
00148 m_uri.Parse(*str);
00149 m_uri.ValidateMem();
00150 m_accum.SetLength(0);
00151 m_state = HTTPREQ_STATE_VERSION;
00152 break;
00153 }
00154 m_accum.Append( ch );
00155 }
00156 break;
00157
00158 case HTTPREQ_STATE_VERSION:
00159 if ( ParseLine(data, len, &pos) )
00160 {
00161 m_httpVersion = *m_accum.ToString();
00162 m_accum.SetLength(0);
00163 m_state = HTTPREQ_STATE_HEADERS;
00164 }
00165 break;
00166
00167 case HTTPREQ_STATE_HEADERS:
00168 if ( ParseLine(data, len, &pos) )
00169 {
00170 if ( m_accum.Length() == 0 )
00171 {
00172 m_state = HTTPREQ_STATE_BODY;
00173 break;
00174 }
00175 StringPtr header = m_accum.ToString();
00176 m_accum.SetLength(0);
00177
00178 int colonPos = header->IndexOf(':');
00179 if ( colonPos < 0 )
00180 {
00181 throw new Exception(String::Format("%s %s", "Invalid HTTP header format", header->GetChars())->GetChars());
00182 }
00183 StringPtr key = header->Substring(0, colonPos);
00184 key = key->Trim()->ToLower();
00185 StringPtr val = header->Substring(colonPos + 1);
00186 m_header.Header(*key) = *val->Trim();
00187 }
00188 break;
00189
00190 case HTTPREQ_STATE_BODY:
00191 if ( NULL == m_body )
00192 {
00193 if ( m_header.HasHeader("Content-Type") )
00194 {
00195 String& contentType = m_header.Header("Content-Type");
00196 if ( contentType.Equals("application/x-www-form-urlencoded") )
00197 {
00198 m_body = new HttpRequestBodyFormData();
00199 }
00200 else
00201 {
00202 m_body = new HttpRequestBodyFormData();
00203 }
00204 }
00205 else
00206 {
00207 m_body = new HttpRequestBodyFormData();
00208 }
00209 }
00210 int contentLen;
00211 if ( m_header.HasHeader("Content-Length") )
00212 {
00213 String& scl = m_header.Header("Content-Length");
00214 ASSERT( Int32::IsInt(scl) );
00215 contentLen = Int32::Parse(scl);
00216 }
00217 else
00218 {
00219 contentLen = 0;
00220 }
00221
00222 m_body->Parse( data, pos, len - pos, contentLen );
00223
00224 break;
00225
00226 default:
00227 throw new Exception("HttpRequest::Parse: corrupted state.");
00228 }
00229 }
00230 }
00231
00232 bool HttpRequest::IsComplete()
00233 {
00234 if ( m_header.Count() == 0 )
00235 {
00236 return false;
00237 }
00238
00239 int contentLen;
00240 if ( m_header.HasHeader("Content-Length") )
00241 {
00242 String& scl = m_header.Header("Content-Length");
00243 ASSERT( Int32::IsInt(scl) );
00244 contentLen = Int32::Parse(scl);
00245 }
00246 else
00247 {
00248 contentLen = 0;
00249 }
00250
00251 if ( NULL != m_body && contentLen > 0 )
00252 {
00253 return m_body->ByteCount() >= contentLen;
00254 }
00255 else
00256 {
00257
00258 return m_state == HTTPREQ_STATE_BODY;
00259 }
00260 }
00261
00262 HttpResponse *HttpRequest::Send()
00263 {
00264 HttpResponse *resp = new HttpResponse();
00265
00266 try
00267 {
00268 TcpSocket sock(m_uri.Host(), m_uri.Port());
00269 sock.Connect();
00270 sock.SetRecvTimeout( 30 * 1000 );
00271 sock.SetBlocking();
00272
00273 if ( NULL == m_body )
00274 {
00275 m_header.ContentLength() = "0";
00276 }
00277 else
00278 {
00279 m_header.ContentLength() = *Int32::ToString(m_body->ByteCount());
00280 }
00281
00282 if ( m_header.ContentType() == "" )
00283 {
00284 m_header.ContentType() = "Content-Type", "application/x-www-form-urlencoded";
00285 }
00286
00287 StringPtr httpReq = ToString();
00288 sock.GetStream()->Write( httpReq->ToByteArray() );
00289
00290 IStreamPtr reader = sock.GetStream();
00291 resp->Parse(reader);
00292 sock.Close();
00293 }
00294 catch ( Exception *ex )
00295 {
00296 delete resp;
00297 throw ex;
00298 }
00299
00300 return resp;
00301 }
00302
00303 StringPtr HttpRequest::ToString() const
00304 {
00305 StringBuffer buf;
00306
00307 buf.Append( m_method );
00308 buf.Append( ' ' );
00309 buf.Append( m_uri.AbsolutePath() );
00310 buf.Append( ' ' );
00311 buf.Append( m_httpVersion );
00312 buf.Append( "\r\n" );
00313 buf.Append( m_header.ToString() );
00314
00315 if ( NULL != m_body )
00316 {
00317 buf.Append( m_body->ToString() );
00318 }
00319
00320 return buf.ToString();
00321 }
00322
00323 #ifdef DEBUG
00324 void HttpRequest::ValidateMem() const
00325 {
00326 m_method.ValidateMem();
00327 m_httpVersion.ValidateMem();
00328 m_accum.ValidateMem();
00329
00330 if ( NULL != m_body )
00331 {
00332 ASSERT_PTR(m_body);
00333 m_body->ValidateMem();
00334 }
00335 }
00336
00337 void HttpRequest::CheckMem() const
00338 {
00339 m_method.CheckMem();
00340 m_httpVersion.CheckMem();
00341 m_accum.CheckMem();
00342
00343 if ( NULL != m_body )
00344 {
00345 DEBUG_NOTE_MEM( m_body );
00346 m_body->CheckMem();
00347 }
00348 }
00349 #endif