/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { llifstream fstream(mFilename, std::ios::binary); if (!fstream.is_open()) throw AICurlNoBody(llformat("Failed to open \"%s\".", mFilename.c_str())); LLBufferStream ostream(channels, buffer.get()); char tmpbuf[4096]; #ifdef SHOW_ASSERT size_t total_len = 0; fstream.seekg(0, std::ios::end); size_t file_size = fstream.tellg(); fstream.seekg(0, std::ios::beg); #endif while (fstream) { fstream.read(tmpbuf, sizeof(tmpbuf)); std::streamsize len = fstream.gcount(); if (len > 0) { ostream.write(tmpbuf, len); #ifdef SHOW_ASSERT total_len += len; #endif } } if (fstream.bad()) throw AICurlNoBody(llformat("An error occured while reading \"%s\".", mFilename.c_str())); fstream.close(); ostream << std::flush; llassert(total_len == file_size && total_len == ostream.count_out()); return ostream.count_out(); }
void EmeraldDicDownloader::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer) { if (status < 200 || status >= 300) { return; } LLBufferStream istr(channels, buffer.get()); std::string dicpath(gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "dictionaries", name.c_str())); llofstream ostr(dicpath, std::ios::binary); while (istr.good() && ostr.good()) { ostr << istr.rdbuf(); } ostr.close(); if (panel) { if (panel->empanel) { panel->empanel->refresh(); } else { llinfos << "completedRaw(): No empanel to refresh()!" << llendl; } panel->close(); } }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { LLBufferStream ostream(channels, buffer.get()); ostream.write(mData, mSize); ostream << std::flush; // Always flush a LLBufferStream when done writing to it. return mSize; }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { LLBufferStream ostream(channels, buffer.get()); LLSDSerialize::toXML(mSD, ostream); // Need to flush the LLBufferStream or count_out() returns more than the written data. ostream << std::flush; return ostream.count_out(); }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { std::string data; mTree->write(data); LLBufferStream ostream(channels, buffer.get()); ostream.write(data.data(), data.size()); ostream << std::flush; // Always flush a LLBufferStream when done writing to it. return data.size(); }
EStatus process_impl(const LLChannelDescriptors &channels, buffer_ptr_t &buffer, bool &eos, LLSD &context, LLPumpIO *pump) { LLBufferStream ostream(channels, buffer.get()); ostream.write(mData.data(), mData.size()); eos = true; return STATUS_DONE; }
// virtual LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LLFastTimer t(FTM_PROCESS_SD2XMLRPC_RESPONSE); PUMP_DEBUG; // This pipe does not work if it does not have everyting. This // could be addressed by making a stream parser for llsd which // handled partial information. if(!eos) { return STATUS_BREAK; } PUMP_DEBUG; // we have everyting in the buffer, so turn the structure data rpc // response into an xml rpc response. LLBufferStream stream(channels, buffer.get()); stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER << std::flush; // Flush, or buffer->count() returns too much! LLSD sd; LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in())); PUMP_DEBUG; LLIOPipe::EStatus rv = STATUS_ERROR; if(sd.has("response")) { PUMP_DEBUG; // it is a normal response. pack it up and ship it out. stream.precision(DEFAULT_PRECISION); stream << XMLRPC_RESPONSE_HEADER; streamOut(stream, sd["response"]); stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER; rv = STATUS_DONE; } else if(sd.has("fault")) { PUMP_DEBUG; // it is a fault. stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger() << XMLRPC_FAULT_2 << xml_escape_string(sd["fault"]["description"].asString()) << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER; rv = STATUS_DONE; } else { llwarns << "Unable to determine the type of LLSD response." << llendl; } PUMP_DEBUG; return rv; }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { int requestTextSize; mRequestText = XMLRPC_REQUEST_ToXML(mRequest, &requestTextSize); if (!mRequestText) throw AICurlNoBody("XMLRPC_REQUEST_ToXML returned NULL."); LLBufferStream ostream(channels, buffer.get()); ostream.write(mRequestText, requestTextSize); ostream << std::flush; // Always flush a LLBufferStream when done writing to it. return requestTextSize; }
void LLHTTPResponder::markBad( const LLChannelDescriptors& channels, buffer_ptr_t buffer) { LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); mState = STATE_SHORT_CIRCUIT; LLBufferStream out(channels, buffer.get()); out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n" << "<title>Bad Request</title>\n<body>\nBad Request.\n" << "</body>\n</html>\n"; }
/*virtual*/ U32 get_body(LLChannelDescriptors const& channels, buffer_ptr_t& buffer) { LLBufferStream ostream(channels, buffer.get()); LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ); S32 fileSize = vfile.getSize(); std::vector<U8> fileBuffer(fileSize); vfile.read(&fileBuffer[0], fileSize); ostream.write((char*)&fileBuffer[0], fileSize); ostream << std::flush; return fileSize; }
virtual EStatus process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { if(!eos) return STATUS_BREAK; LLSD sd = "yo!"; LLBufferStream ostr(channels, buffer.get()); ostr << LLSDXMLStreamer(sd); return STATUS_DONE; }
/*virtual*/ void completedRaw( U32 status, const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer) { LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); const std::string body = strstrm.str(); if (status != 200) { llwarns << "Failed to get upload config (" << status << ")" << llendl; LLWebProfile::reportImageUploadStatus(false); return; } Json::Value root; Json::Reader reader; if (!reader.parse(body, root)) { llwarns << "Failed to parse upload config: " << reader.getFormatedErrorMessages() << llendl; LLWebProfile::reportImageUploadStatus(false); return; } // *TODO: 404 = not supported by the grid // *TODO: increase timeout or handle 499 Expired // Convert config to LLSD. const Json::Value data = root["data"]; const std::string upload_url = root["url"].asString(); LLSD config; config["acl"] = data["acl"].asString(); config["AWSAccessKeyId"] = data["AWSAccessKeyId"].asString(); config["Content-Type"] = data["Content-Type"].asString(); config["key"] = data["key"].asString(); config["policy"] = data["policy"].asString(); config["success_action_redirect"] = data["success_action_redirect"].asString(); config["signature"] = data["signature"].asString(); config["add_loc"] = data.get("add_loc", "0").asString(); config["caption"] = data.get("caption", "").asString(); // Do the actual image upload using the configuration. LL_DEBUGS("Snapshots") << "Got upload config, POSTing image to " << upload_url << ", config=[" << config << "]" << LL_ENDL; LLWebProfile::post(mImagep, config, upload_url); }
/*virtual*/ void completedRaw(LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) { if (mStatus != HTTP_OK) { LL_WARNS() << "Failed to upload image: " << mStatus << " " << mReason << LL_ENDL; LLWebProfile::reportImageUploadStatus(false); return; } LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); const std::string body = strstrm.str(); LL_INFOS() << "Image uploaded." << LL_ENDL; LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL; LLWebProfile::reportImageUploadStatus(true); }
// virtual LLIOPipe::EStatus LLIOSleeper::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { if(!mRespond) { LL_DEBUGS() << "LLIOSleeper::process_impl() sleeping." << LL_ENDL; mRespond = true; static const F64 SLEEP_TIME = 2.0; pump->sleepChain(SLEEP_TIME); return STATUS_BREAK; } LL_DEBUGS() << "LLIOSleeper::process_impl() responding." << LL_ENDL; LLBufferStream ostr(channels, buffer.get()); ostr << "huh? sorry, I was sleeping." << std::endl; return STATUS_DONE; }
/*virtual*/ void completedRaw( U32 status, const std::string& reason, const LLChannelDescriptors& channels, const buffer_ptr_t& buffer) { if (status != 200) { llwarns << "Failed to upload image: " << status << " " << reason << llendl; LLWebProfile::reportImageUploadStatus(false); return; } LLBufferStream istr(channels, buffer.get()); std::stringstream strstrm; strstrm << istr.rdbuf(); const std::string body = strstrm.str(); llinfos << "Image uploaded." << llendl; LL_DEBUGS("Snapshots") << "Uploading image succeeded. Response: [" << body << "]" << LL_ENDL; LLWebProfile::reportImageUploadStatus(true); }
LLIOPipe::EStatus LLPipeStringExtractor::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { if(!eos) return STATUS_BREAK; if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; LLBufferStream istr(channels, buffer.get()); std::ostringstream ostr; while (istr.good()) { char buf[1024]; /* Flawfinder: ignore */ istr.read(buf, sizeof(buf)); /* Flawfinder: ignore */ ostr.write(buf, istr.gcount()); } mString = ostr.str(); mDone = true; return STATUS_DONE; }
LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_RESPONSE); PUMP_DEBUG; if(!eos) return STATUS_BREAK; if(!buffer) return STATUS_ERROR; PUMP_DEBUG; // *FIX: This technique for reading data is far from optimal. We // need to have some kind of istream interface into the xml // parser... S32 bytes = buffer->countAfter(channels.in(), NULL); if(!bytes) return STATUS_ERROR; char* buf = new char[bytes + 1]; buf[bytes] = '\0'; buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes); //lldebugs << "xmlrpc response: " << buf << llendl; PUMP_DEBUG; XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML( buf, bytes, NULL); if(!response) { llwarns << "XML -> SD Response unable to parse xml." << llendl; delete[] buf; return STATUS_ERROR; } PUMP_DEBUG; LLBufferStream stream(channels, buffer.get()); stream.precision(DEFAULT_PRECISION); if(XMLRPC_ResponseIsFault(response)) { PUMP_DEBUG; stream << LLSDRPC_FAULT_HADER_1 << XMLRPC_GetResponseFaultCode(response) << LLSDRPC_FAULT_HADER_2; const char* fault_str = XMLRPC_GetResponseFaultString(response); std::string fault_string; if(fault_str) { fault_string.assign(fault_str); } stream << "'" << LLSDNotationFormatter::escapeString(fault_string) << "'" <<LLSDRPC_FAULT_FOOTER << std::flush; } else { PUMP_DEBUG; stream << LLSDRPC_RESPONSE_HEADER; XMLRPC_VALUE param = XMLRPC_RequestGetData(response); if(param) { stream_out(stream, param); } stream << LLSDRPC_RESPONSE_FOOTER << std::flush; } PUMP_DEBUG; XMLRPC_RequestFree(response, 1); delete[] buf; PUMP_DEBUG; return STATUS_DONE; }
// virtual LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LLFastTimer t(FTM_PROCESS_SD2XMLRPC_REQUEST); // This pipe does not work if it does not have everyting. This // could be addressed by making a stream parser for llsd which // handled partial information. PUMP_DEBUG; if(!eos) { llinfos << "!eos" << llendl; return STATUS_BREAK; } // See if we can parse it LLBufferStream stream(channels, buffer.get()); LLSD sd; LLSDSerialize::fromNotation(sd, stream, buffer->count(channels.in())); if(stream.fail()) { llinfos << "STREAM FAILURE reading structure data." << llendl; } PUMP_DEBUG; // We can get the method and parameters from either the member // function or passed in via the buffer. We prefer the buffer if // we found a parameter and a method, or fall back to using // mMethod and putting everyting in the buffer into the parameter. std::string method; LLSD param_sd; if(sd.has("method") && sd.has("parameter")) { method = sd["method"].asString(); param_sd = sd["parameter"]; } else { method = mMethod; param_sd = sd; } if(method.empty()) { llwarns << "SD -> XML Request no method found." << llendl; return STATUS_ERROR; } PUMP_DEBUG; // We have a method, and some kind of parameter, so package it up // and send it out. LLBufferStream ostream(channels, buffer.get()); ostream.precision(DEFAULT_PRECISION); if(ostream.fail()) { llinfos << "STREAM FAILURE setting precision" << llendl; } ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1 << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2; if(ostream.fail()) { llinfos << "STREAM FAILURE writing method headers" << llendl; } switch(param_sd.type()) { case LLSD::TypeMap: // If the params are a map, then we do not want to iterate // through them since the iterators returned will be map // ordered un-named values, which will lose the names, and // only stream the values, turning it into an array. ostream << "<param>"; streamOut(ostream, param_sd); ostream << "</param>"; break; case LLSD::TypeArray: { LLSD::array_iterator it = param_sd.beginArray(); LLSD::array_iterator end = param_sd.endArray(); for(; it != end; ++it) { ostream << "<param>"; streamOut(ostream, *it); ostream << "</param>"; } break; } default: ostream << "<param>"; streamOut(ostream, param_sd); ostream << "</param>"; break; } stream << XMLRPC_REQUEST_FOOTER << std::flush; return STATUS_DONE; }
LLIOPipe::EStatus LLHTTPPipe::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { PUMP_DEBUG; lldebugs << "LLSDHTTPServer::process_impl" << llendl; // Once we have all the data, We need to read the sd on // the the in channel, and respond on the out channel if(!eos) return STATUS_BREAK; if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; PUMP_DEBUG; if (mState == STATE_INVOKE) { PUMP_DEBUG; mState = STATE_DELAYED; // assume deferred unless mResponse does otherwise mResponse = Response::create(this); // *TODO: Babbage: Parameterize parser? // *TODO: We should look at content-type and do the right // thing. Phoenix 2007-12-31 LLBufferStream istr(channels, buffer.get()); static LLTimer timer; timer.reset(); std::string verb = context[CONTEXT_REQUEST][CONTEXT_VERB]; if(verb == HTTP_VERB_GET) { LLPerfBlock getblock("http_get"); mNode.get(LLHTTPNode::ResponsePtr(mResponse), context); } else if(verb == HTTP_VERB_PUT) { LLPerfBlock putblock("http_put"); LLSD input; LLSDSerialize::fromXML(input, istr); mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_POST) { LLPerfBlock postblock("http_post"); LLSD input; LLSDSerialize::fromXML(input, istr); mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_DELETE) { LLPerfBlock delblock("http_delete"); mNode.del(LLHTTPNode::ResponsePtr(mResponse), context); } else if(verb == HTTP_VERB_OPTIONS) { mNode.options(LLHTTPNode::ResponsePtr(mResponse), context); } else { mResponse->methodNotAllowed(); } F32 delta = timer.getElapsedTimeF32(); if (sTimingCallback) { LLHTTPNode::Description desc; mNode.describe(desc); LLSD info = desc.getInfo(); std::string timing_name = info["description"]; timing_name += " "; timing_name += verb; sTimingCallback(timing_name.c_str(), delta, sTimingCallbackData); } // Log all HTTP transactions. // TODO: Add a way to log these to their own file instead of indra.log // It is just too spammy to be in indra.log. lldebugs << verb << " " << context[CONTEXT_REQUEST]["path"].asString() << " " << mStatusCode << " " << mStatusMessage << " " << delta << "s" << llendl; // Log Internal Server Errors //if(mStatusCode == 500) //{ // llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error" // << llendl; //} } PUMP_DEBUG; switch (mState) { case STATE_DELAYED: lockChain(pump); mState = STATE_LOCKED; return STATUS_BREAK; case STATE_LOCKED: // should never ever happen! return STATUS_ERROR; case STATE_GOOD_RESULT: { LLSD headers = mHeaders; headers["Content-Type"] = "application/llsd+xml"; context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; LLBufferStream ostr(channels, buffer.get()); LLSDSerialize::toXML(mGoodResult, ostr); return STATUS_DONE; } case STATE_STATUS_RESULT: { LLSD headers = mHeaders; headers["Content-Type"] = "text/plain"; context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode; context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage; LLBufferStream ostr(channels, buffer.get()); ostr << mStatusMessage << std::ends; return STATUS_DONE; } default: llwarns << "LLHTTPPipe::process_impl: unexpected state " << mState << llendl; return STATUS_BREAK; } // PUMP_DEBUG; // unreachable }
// virtual LLIOPipe::EStatus LLURLRequest::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; if (!buffer) return STATUS_ERROR; // we're still waiting or prcessing, check how many // bytes we have accumulated. const S32 MIN_ACCUMULATION = 100000; if(pump && (mDetail->mByteAccumulator > MIN_ACCUMULATION)) { // This is a pretty sloppy calculation, but this // tries to make the gross assumption that if data // is coming in at 56kb/s, then this transfer will // probably succeed. So, if we're accumlated // 100,000 bytes (MIN_ACCUMULATION) then let's // give this client another 2s to complete. const F32 TIMEOUT_ADJUSTMENT = 2.0f; mDetail->mByteAccumulator = 0; pump->adjustTimeoutSeconds(TIMEOUT_ADJUSTMENT); lldebugs << "LLURLRequest adjustTimeoutSeconds for request: " << mDetail->mURL << llendl; if (mState == STATE_INITIALIZED) { llinfos << "LLURLRequest adjustTimeoutSeconds called during upload" << llendl; } } switch(mState) { case STATE_INITIALIZED: { PUMP_DEBUG; // We only need to wait for input if we are uploading // something. if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos) { // we're waiting to get all of the information return STATUS_BREAK; } // *FIX: bit of a hack, but it should work. The configure and // callback method expect this information to be ready. mDetail->mResponseBuffer = buffer.get(); mDetail->mChannels = channels; if(!configure()) { return STATUS_ERROR; } mState = STATE_WAITING_FOR_RESPONSE; // *FIX: Maybe we should just go to the next state now... return STATUS_BREAK; } case STATE_WAITING_FOR_RESPONSE: case STATE_PROCESSING_RESPONSE: { PUMP_DEBUG; LLIOPipe::EStatus status = STATUS_BREAK; mDetail->mCurlRequest->perform(); while(1) { CURLcode result; bool newmsg = mDetail->mCurlRequest->getResult(&result); if(!newmsg) { // keep processing break; } mState = STATE_HAVE_RESPONSE; context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; lldebugs << this << "Setting context to " << context << llendl; switch(result) { case CURLE_OK: case CURLE_WRITE_ERROR: // NB: The error indication means that we stopped the // writing due the body limit being reached if(mCompletionCallback && pump) { LLURLRequestComplete* complete = NULL; complete = (LLURLRequestComplete*) mCompletionCallback.get(); complete->responseStatus( result == CURLE_OK ? STATUS_OK : STATUS_STOP); LLPumpIO::links_t chain; LLPumpIO::LLLinkInfo link; link.mPipe = mCompletionCallback; link.mChannels = LLBufferArray::makeChannelConsumer( channels); chain.push_back(link); pump->respond(chain, buffer, context); mCompletionCallback = NULL; } break; case CURLE_FAILED_INIT: case CURLE_COULDNT_CONNECT: status = STATUS_NO_CONNECTION; break; default: llwarns << "URLRequest Error: " << result << ", " << LLCurl::strerror(result) << ", " << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL) << llendl; status = STATUS_ERROR; break; } } return status; } case STATE_HAVE_RESPONSE: PUMP_DEBUG; // we already stuffed everything into channel in in the curl // callback, so we are done. eos = true; context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; lldebugs << this << "Setting context to " << context << llendl; return STATUS_DONE; default: PUMP_DEBUG; context[CONTEXT_REQUEST][CONTEXT_TRANSFERED_BYTES] = mRequestTransferedBytes; context[CONTEXT_RESPONSE][CONTEXT_TRANSFERED_BYTES] = mResponseTransferedBytes; lldebugs << this << "Setting context to " << context << llendl; return STATUS_ERROR; } }
// virtual LLIOPipe::EStatus LLSDRPCClient::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT); if((STATE_NONE == mState) || (!pump)) { // You should have called the call() method already. return STATUS_PRECONDITION_NOT_MET; } EStatus rv = STATUS_DONE; switch(mState) { case STATE_READY: { PUMP_DEBUG; // lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl; buffer->append( channels.out(), (U8*)mRequest.c_str(), mRequest.length()); context[CONTEXT_DEST_URI_SD_LABEL] = mURI; mState = STATE_WAITING_FOR_RESPONSE; break; } case STATE_WAITING_FOR_RESPONSE: { PUMP_DEBUG; // The input channel has the sd response in it. //lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE" // << llendl; LLBufferStream resp(channels, buffer.get()); LLSD sd; LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in())); LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get(); if (!response) { mState = STATE_DONE; break; } response->extractResponse(sd); if(EPBQ_PROCESS == mQueue) { LLPumpIO::chain_t chain; chain.push_back(mResponse); pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS); } else { pump->respond(mResponse.get()); } mState = STATE_DONE; break; } case STATE_DONE: default: PUMP_DEBUG; llinfos << "invalid state to process" << llendl; rv = STATUS_ERROR; break; } return rv; }
LLIOPipe::EStatus LLHTTPPipe::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { PUMP_DEBUG; lldebugs << "LLSDHTTPServer::process_impl" << llendl; // Once we have all the data, We need to read the sd on // the the in channel, and respond on the out channel if(!eos) return STATUS_BREAK; if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; PUMP_DEBUG; if (mState == STATE_INVOKE) { PUMP_DEBUG; mState = STATE_DELAYED; // assume deferred unless mResponse does otherwise mResponse = Response::create(this); // TODO: Babbage: Parameterize parser? LLBufferStream istr(channels, buffer.get()); static LLTimer timer; if (sTimingCallback) { timer.reset(); } std::string verb = context[CONTEXT_REQUEST]["verb"]; if(verb == HTTP_VERB_GET) { mNode.get(LLHTTPNode::ResponsePtr(mResponse), context); } else if(verb == HTTP_VERB_PUT) { LLSD input; LLSDSerialize::fromXML(input, istr); mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_POST) { LLSD input; LLSDSerialize::fromXML(input, istr); mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input); } else if(verb == HTTP_VERB_DELETE) { mNode.del(LLHTTPNode::ResponsePtr(mResponse), context); } else { mResponse->methodNotAllowed(); } if (sTimingCallback) { LLHTTPNode::Description desc; mNode.describe(desc); LLSD info = desc.getInfo(); std::string timing_name = info["description"]; timing_name += " "; timing_name += verb; F32 delta = timer.getElapsedTimeF32(); sTimingCallback(timing_name.c_str(), delta, sTimingCallbackData); } // Log Internal Server Errors if(mStatusCode == 500) { llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error" << llendl; } } PUMP_DEBUG; switch (mState) { case STATE_DELAYED: lockChain(pump); mState = STATE_LOCKED; return STATUS_BREAK; case STATE_LOCKED: // should never ever happen! return STATUS_ERROR; case STATE_GOOD_RESULT: { context["response"]["contentType"] = "application/xml"; LLBufferStream ostr(channels, buffer.get()); LLSDSerialize::toXML(mGoodResult, ostr); return STATUS_DONE; } case STATE_STATUS_RESULT: { context["response"]["contentType"] = "text/plain"; context["response"]["statusCode"] = mStatusCode; context["response"]["statusMessage"] = mStatusMessage; LLBufferStream ostr(channels, buffer.get()); ostr << mStatusMessage << std::ends; return STATUS_DONE; } default: llwarns << "LLHTTPPipe::process_impl: unexpected state " << mState << llendl; return STATUS_BREAK; } // PUMP_DEBUG; // unreachable }
LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LLFastTimer t(FTM_PROCESS_XMLRPC2LLSD_REQUEST); PUMP_DEBUG; if(!eos) return STATUS_BREAK; if(!buffer) return STATUS_ERROR; PUMP_DEBUG; // *FIX: This technique for reading data is far from optimal. We // need to have some kind of istream interface into the xml // parser... S32 bytes = buffer->countAfter(channels.in(), NULL); if(!bytes) return STATUS_ERROR; char* buf = new char[bytes + 1]; buf[bytes] = '\0'; buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes); //lldebugs << "xmlrpc request: " << buf << llendl; // Check the value in the buffer. XMLRPC_REQUEST_FromXML will report a error code 4 if // values that are less than 0x20 are passed to it, except // 0x09: Horizontal tab; 0x0a: New Line; 0x0d: Carriage U8* cur_pBuf = (U8*)buf; U8 cur_char; for (S32 i=0; i<bytes; i++) { cur_char = *cur_pBuf; if ( cur_char < 0x20 && 0x09 != cur_char && 0x0a != cur_char && 0x0d != cur_char ) { *cur_pBuf = '?'; } ++cur_pBuf; } PUMP_DEBUG; XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML( buf, bytes, NULL); if(!request) { llwarns << "XML -> SD Request process parse error." << llendl; delete[] buf; return STATUS_ERROR; } PUMP_DEBUG; LLBufferStream stream(channels, buffer.get()); stream.precision(DEFAULT_PRECISION); const char* name = XMLRPC_RequestGetMethodName(request); stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "") << LLSDRPC_REQUEST_HEADER_2; XMLRPC_VALUE param = XMLRPC_RequestGetData(request); if(param) { PUMP_DEBUG; S32 size = XMLRPC_VectorSize(param); if(size > 1) { // if there are multiple parameters, stuff the values into // an array so that the next step in the chain can read them. stream << "["; } XMLRPC_VALUE current = XMLRPC_VectorRewind(param); bool needs_comma = false; while(current) { if(needs_comma) { stream << ","; } needs_comma = true; stream_out(stream, current); current = XMLRPC_VectorNext(param); } if(size > 1) { // close the array stream << "]"; } } stream << LLSDRPC_REQUEST_FOOTER << std::flush; XMLRPC_RequestFree(request, 1); delete[] buf; PUMP_DEBUG; return STATUS_DONE; }
// virtual LLIOPipe::EStatus LLSDRPCServer::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { LLFastTimer t(FTM_PROCESS_SDRPC_SERVER); PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER); // lldebugs << "LLSDRPCServer::process_impl" << llendl; // Once we have all the data, We need to read the sd on // the the in channel, and respond on the out channel if(!eos) return STATUS_BREAK; if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; std::string method_name; LLIOPipe::EStatus status = STATUS_DONE; switch(mState) { case STATE_DEFERRED: PUMP_DEBUG; if(ESDRPCS_DONE != deferredResponse(channels, buffer.get())) { buildFault( channels, buffer.get(), FAULT_GENERIC, "deferred response failed."); } mState = STATE_DONE; return STATUS_DONE; case STATE_DONE: // lldebugs << "STATE_DONE" << llendl; break; case STATE_CALLBACK: // lldebugs << "STATE_CALLBACK" << llendl; PUMP_DEBUG; method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) { if(ESDRPCS_DONE != callbackMethod( method_name, mRequest[LLSDRPC_PARAMETER_SD_NAME], channels, buffer.get())) { buildFault( channels, buffer.get(), FAULT_GENERIC, "Callback method call failed."); } } else { // this should never happen, since we should not be in // this state unless we originally found a method and // params during the first call to process. buildFault( channels, buffer.get(), FAULT_GENERIC, "Invalid LLSDRPC sever state - callback without method."); } pump->clearLock(mLock); mLock = 0; mState = STATE_DONE; break; case STATE_NONE: // lldebugs << "STATE_NONE" << llendl; default: { // First time we got here - process the SD request, and call // the method. PUMP_DEBUG; LLBufferStream istr(channels, buffer.get()); mRequest.clear(); LLSDSerialize::fromNotation( mRequest, istr, buffer->count(channels.in())); // { 'method':'...', 'parameter': ... } method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString(); if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME)) { ESDRPCSStatus rv = callMethod( method_name, mRequest[LLSDRPC_PARAMETER_SD_NAME], channels, buffer.get()); switch(rv) { case ESDRPCS_DEFERRED: mPump = pump; mLock = pump->setLock(); mState = STATE_DEFERRED; status = STATUS_BREAK; break; case ESDRPCS_CALLBACK: { mState = STATE_CALLBACK; LLPumpIO::LLLinkInfo link; link.mPipe = LLIOPipe::ptr_t(this); link.mChannels = channels; LLPumpIO::links_t links; links.push_back(link); pump->respond(links, buffer, context); mLock = pump->setLock(); status = STATUS_BREAK; break; } case ESDRPCS_DONE: mState = STATE_DONE; break; case ESDRPCS_ERROR: default: buildFault( channels, buffer.get(), FAULT_GENERIC, "Method call failed."); break; } } else { // send a fault buildFault( channels, buffer.get(), FAULT_GENERIC, "Unable to find method and parameter in request."); } break; } } PUMP_DEBUG; return status; }
// virtual LLIOPipe::EStatus LLURLRequest::process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump) { PUMP_DEBUG; LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); //llinfos << "LLURLRequest::process_impl()" << llendl; if(!buffer) return STATUS_ERROR; switch(mState) { case STATE_INITIALIZED: { PUMP_DEBUG; // We only need to wait for input if we are uploading // something. if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos) { // we're waiting to get all of the information return STATUS_BREAK; } // *FIX: bit of a hack, but it should work. The configure and // callback method expect this information to be ready. mDetail->mResponseBuffer = buffer.get(); mDetail->mChannels = channels; if(!configure()) { return STATUS_ERROR; } mState = STATE_WAITING_FOR_RESPONSE; // *FIX: Maybe we should just go to the next state now... return STATUS_BREAK; } case STATE_WAITING_FOR_RESPONSE: case STATE_PROCESSING_RESPONSE: { PUMP_DEBUG; LLIOPipe::EStatus status = STATUS_BREAK; mDetail->mCurlRequest->perform(); while(1) { CURLcode result; bool newmsg = mDetail->mCurlRequest->getResult(&result); if (!newmsg) { break; } mState = STATE_HAVE_RESPONSE; switch(result) { case CURLE_OK: case CURLE_WRITE_ERROR: // NB: The error indication means that we stopped the // writing due the body limit being reached if(mCompletionCallback && pump) { LLURLRequestComplete* complete = NULL; complete = (LLURLRequestComplete*) mCompletionCallback.get(); complete->responseStatus( result == CURLE_OK ? STATUS_OK : STATUS_STOP); LLPumpIO::links_t chain; LLPumpIO::LLLinkInfo link; link.mPipe = mCompletionCallback; link.mChannels = LLBufferArray::makeChannelConsumer( channels); chain.push_back(link); pump->respond(chain, buffer, context); mCompletionCallback = NULL; } break; case CURLE_FAILED_INIT: case CURLE_COULDNT_CONNECT: status = STATUS_NO_CONNECTION; break; default: llwarns << "URLRequest Error: " << result << ", " << LLCurl::strerror(result) << ", " << (mDetail->mURL.empty() ? "<EMPTY URL>" : mDetail->mURL) << llendl; status = STATUS_ERROR; break; } } return status; } case STATE_HAVE_RESPONSE: PUMP_DEBUG; // we already stuffed everything into channel in in the curl // callback, so we are done. eos = true; return STATUS_DONE; default: PUMP_DEBUG; return STATUS_ERROR; } }