DynamicObject ValidatorContext::addError( const char* type, DynamicObject* object) { DynamicObject errorDetail; // setup error detail errorDetail["type"] = type; // FIXME: localize message -- lehn // FIXME: really? do we need to mention this, because we'd have to // do this for every string in the system.. -- manu errorDetail["message"] = "The given value does not meet all of the data " "validation requirements. Please examine the error details for more " "information about the specific requirements."; if(object != NULL && (mMaskType & ValidatorContext::MaskInvalidValues) == 0) { errorDetail["invalidValue"] = *object; } // add error detail to results errors std::string fullpath = getPath(); mResults["errors"][fullpath.c_str()] = errorDetail; // Skip setting exceptions if requested. Return errorDetail regardless. if(mSetExceptions) { ExceptionRef e; if(!Exception::isSet()) { e = new Exception( "The given object does not meet all of the data validation " "requirements. Please examine the error details for more " "information about the specific requirements.", "monarch.validation.ValidationError"); Exception::set(e); } else { e = Exception::get(); // Check if we are adding to a ValidationError if(!e->isType("monarch.validation.ValidationError")) { // FIXME: this is a bit bogus. If validation checking keeps causing // other exceptions then a long cause chain could be generated // switching between ValidationError and other types. e = new Exception( "The given object does not meet all of the data validation " "requirements. Please examine the error details for more " "information about the specific requirements.", "monarch.validation.ValidationError"); Exception::push(e); } } // add detail to "errors" section of exception details e->getDetails()["errors"][fullpath.c_str()] = errorDetail; } return errorDetail; }
ExceptionRef Exception::getExceptionOfType( ExceptionRef& e, const char* type, bool startsWith) { ExceptionRef rval(NULL); if(e->isType(type, startsWith)) { rval = e; } else { rval = e->getCauseOfType(type, startsWith); } return rval; }
int DeviceDiscoverer::discover( DeviceList& devices, const char* searchTarget, uint32_t timeout, int count) { int rval = -1; // prepare device list devices->setType(Array); devices->clear(); // create SSDP request HttpRequestHeader requestHeader; createRequest(searchTarget, &requestHeader); // create socket for sending request DatagramSocket socket; // bind to any available port InternetAddressRef localAddr = new InternetAddress("0.0.0.0", 0); if(socket.bind(&(*localAddr))) { // create the group address InternetAddressRef groupAddr = new InternetAddress( SSDP_MULTICAST_ADDRESS, SSDP_MULTICAST_PORT); // create and send discover request datagram DatagramRef request = new Datagram(groupAddr); request->assignString(requestHeader.toString().c_str()); MO_CAT_DEBUG(MO_UPNP_CAT, "Sending UPnP request:\n%s", requestHeader.toString().c_str()); if(socket.send(request)) { // no devices yet rval = 0; // use timer to comply with user-supplied timeout Timer timer; timer.start(); uint32_t remaining = timeout; InternetAddressRef addr = new InternetAddress(); while(rval >= 0 && remaining > 0 && (count == 0 || rval < count)) { // set receive timeout and try to get ssdp responses socket.setReceiveTimeout(remaining); DatagramRef response = new Datagram(addr); response->getBuffer()->resize(2048); if(!socket.receive(response)) { // check last exception ExceptionRef e = Exception::get(); if(e->isType("monarch.net.SocketTimeout")) { MO_CAT_DEBUG(MO_UPNP_CAT, "UPnP request timed out."); // exception indicates timed out remaining = 0; } else { MO_CAT_ERROR(MO_UPNP_CAT, "UPnP request error: %s", JsonWriter::writeToString( Exception::getAsDynamicObject()).c_str()); // some error other than a timeout rval = -1; } } else { // parse ssdp response MO_CAT_DEBUG(MO_UPNP_CAT, "Received UPnP response:\n%s", response->getString().c_str()); Device device = parseDevice(response->getString().c_str()); if(device.isNull()) { MO_CAT_ERROR(MO_UPNP_CAT, "UPnP response parse error: %s", JsonWriter::writeToString( Exception::getAsDynamicObject()).c_str()); // error in parsing rval = -1; } else { MO_CAT_DEBUG(MO_UPNP_CAT, "Found UPnP device: %s", JsonWriter::writeToString(device).c_str()); // another device found ++rval; devices->append(device); // update remaining time (must be within 32-bit integer range) remaining = (uint32_t)timer.getRemainingMilliseconds(timeout); } } } } } return rval; }
void ListingUpdater::sendUpdate(SellerListingUpdate& update) { MO_CAT_DEBUG(BM_CUSTOMCATALOG_CAT, "ListingUpdater sending seller listing update for user %" PRIu64 ": %s", getUserId(), JsonWriter::writeToString(update).c_str()); // create message to send after update DynamicObject msg; msg["updateResponse"] = true; msg["scheduleUpdateRequest"] = false; msg["error"] = false; // post to bitmunk DynamicObject result; Url url("/api/3.0/catalog/listings"); Messenger* m = mNode->getMessenger(); if(m->postSecureToBitmunk(&url, &update, &result, getUserId())) { // send result in message, include update ID separately so that // handling heartbeats above is simpler -- it doesn't matter if // the current update ID was valid or not, it will be stored in // "updateId" field msg["update"] = update; msg["updateResult"] = result; msg["updateId"] = result["updateId"]->getString(); // if the update wasn't a heartbeat, include a note that another // update should be run immediately after this result is processed if(update["payeeSchemes"]["updates"]->length() > 0 || update["payeeSchemes"]["removals"]->length() > 0 || update["listings"]["updates"]->length() > 0 || update["listings"]["removals"]->length() > 0) { msg["scheduleUpdateRequest"] = true; } } else { // if the error was an invalid update ID, do not record it as an error // and see if we can recover from it ExceptionRef ex = Exception::get(); if(ex->isType("bitmunk.mastercatalog.UpdateIdNotCurrent")) { msg["updateIdNotCurrent"] = true; msg["updateId"] = ex->getDetails()["currentUpdateId"]->getString(); } else { // FIXME: determine if error is a network error or if there // were just some bad wares that weren't accepted/bad remote update ID // failed to update ExceptionRef e = new Exception( "Could not send seller listing update.", "bitmunk.catalog.SellerListingUpdateError"); Exception::push(e); DynamicObject d = Exception::getAsDynamicObject(); msg["error"] = true; msg["exception"] = d; // see if we need to register for a new server ID because our catalog // expired and we no longer exist as a seller // FIXME: we want a different exception name for this if(ex->hasType("bitmunk.database.NotFound") || ex->hasType("bitmunk.database.Catalog.InvalidServerToken")) { // log message ... do new server registration MO_CAT_DEBUG(BM_CUSTOMCATALOG_CAT, "ListingUpdater user's (user ID %" PRIu64 ") seller does not exist, " "has expired, or server token is invalid: %s", getUserId(), JsonWriter::writeToString(d).c_str()); msg["reset"] = true; } else { // log generic error MO_CAT_ERROR(BM_CUSTOMCATALOG_CAT, "ListingUpdater: %s", JsonWriter::writeToString(d).c_str()); } } } // send message to self messageSelf(msg); // schedule a pending test net access request if(mTestNetAccessPending) { mTestNetAccessPending = false; DynamicObject msg; msg["testNetAccess"] = true; messageSelf(msg); } }
void HttpConnectionServicer::serviceConnection(Connection* c) { // wrap connection, set default timeouts to 30 seconds HttpConnection hc(c, false); hc.setReadTimeout(30000); hc.setWriteTimeout(30000); // monitor connection if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeServicingConnection(&hc); } // create request HttpRequest* request = hc.createRequest(); HttpRequestHeader* reqHeader = request->getHeader(); // create response HttpResponse* response = request->createResponse(); HttpResponseHeader* resHeader = response->getHeader(); // handle keep-alive (HTTP/1.1 keep-alive is on by default) bool keepAlive = true; bool noerror = true; while(keepAlive && noerror) { // set defaults resHeader->setVersion("HTTP/1.1"); resHeader->setDate(); resHeader->setField("Server", mServerName); // monitor request waiting if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeRequest(&hc); } // receive request header if((noerror = request->receiveHeader())) { // begin new request state hc.getRequestState()->beginRequest(); // monitor received request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeServicingRequest( &hc, request, response); } // do request modification if(mRequestModifier != NULL) { mRequestModifier->modifyRequest(request); } // check http version bool version10 = (strcmp(reqHeader->getVersion(), "HTTP/1.0") == 0); bool version11 = (strcmp(reqHeader->getVersion(), "HTTP/1.1") == 0); // only version 1.0 and 1.1 supported if(version10 || version11) { // set response version according to request version resHeader->setVersion(reqHeader->getVersion()); // use proxy'd host field if one was used // else use host field if one was used string host; if(reqHeader->getField("X-Forwarded-Host", host) || reqHeader->getField("Host", host)) { resHeader->setField("Host", host); } // get connection header string connHeader; if(reqHeader->getField("Connection", connHeader)) { if(strcasecmp(connHeader.c_str(), "close") == 0) { keepAlive = false; } else if(strcasecmp(connHeader.c_str(), "keep-alive") == 0) { keepAlive = true; } } else if(version10) { // if HTTP/1.0 and no keep-alive header, keep-alive is off keepAlive = false; } // get request path and normalize it const char* inPath = reqHeader->getPath(); char outPath[strlen(inPath) + 2]; HttpRequestServicer::normalizePath(inPath, outPath); // find appropriate request servicer for path HttpRequestServicer* hrs = NULL; // find secure/non-secure servicer hrs = findRequestServicer(host, outPath, hc.isSecure()); if(hrs != NULL) { // service request hrs->serviceRequest(request, response); // turn off keep-alive if response has close connection field if(keepAlive) { if(resHeader->getField("Connection", connHeader) && strcasecmp(connHeader.c_str(), "close") == 0) { keepAlive = false; } } // if servicer closed connection, turn off keep-alive if(keepAlive && c->isClosed()) { keepAlive = false; } } else { // no servicer, so send 404 Not Found const char* html = "<html><body><h2>404 Not Found</h2></body></html>"; resHeader->setStatus(404, "Not Found"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 48); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 48); noerror = response->sendBody(&is); } } } else { // send 505 HTTP Version Not Supported const char* html = "<html><body>" "<h2>505 HTTP Version Not Supported</h2>" "</body></html>"; resHeader->setStatus(505, "HTTP Version Not Supported"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 65); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 65); noerror = response->sendBody(&is); } } // monitor serviced request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterServicingRequest( &hc, request, response); } } else { // begin new request state hc.getRequestState()->beginRequest(); // monitor request error if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeRequestError( &hc, request, response); } // exception occurred while receiving header ExceptionRef e = Exception::get(); // if no header then drop through and close connection if(e->isType("monarch.http.NoHeader")) { keepAlive = false; } // for bad header or request set 400 else if(e->isType("monarch.http.BadHeader") || e->isType("monarch.http.BadRequest")) { // send 400 Bad Request const char* html = "<html><body><h2>400 Bad Request</h2></body></html>"; response->getHeader()->setStatus(400, "Bad Request"); response->getHeader()->setField("Content-Type", "text/html"); response->getHeader()->setField("Content-Length", 50); response->getHeader()->setField("Connection", "close"); if(response->sendHeader()) { ByteArrayInputStream is(html, 50); response->sendBody(&is); } } // if the exception was an interruption, then send a 503 else if(e->isType("monarch.io.InterruptedException") || e->isType("monarch.rt.Interrupted")) { // send 503 Service Unavailable const char* html = "<html><body><h2>503 Service Unavailable</h2></body></html>"; resHeader->setStatus(503, "Service Unavailable"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 58); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 58); noerror = response->sendBody(&is); } } // if the exception was not a socket error then send an internal // server error response else if(!e->isType("monarch.net.Socket", true)) { // send 500 Internal Server Error const char* html = "<html><body><h2>500 Internal Server Error</h2></body></html>"; resHeader->setStatus(500, "Internal Server Error"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 60); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 60); noerror = response->sendBody(&is); } } else { // log socket error if(e->getDetails()->hasMember("error")) { // build error string string error; DynamicObjectIterator i = e->getDetails()["error"].getIterator(); while(i->hasNext()) { error.append(i->next()->getString()); if(i->hasNext()) { error.push_back(','); } } MO_CAT_ERROR(MO_HTTP_CAT, "Connection error: ['%s','%s','%s']", e->getMessage(), e->getType(), error.c_str()); } else { MO_CAT_ERROR(MO_HTTP_CAT, "Connection error: ['%s','%s']", e->getMessage(), e->getType()); } } // monitor request error if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterRequestError( &hc, request, response, e); } } // monitor request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterRequest(&hc); } if(keepAlive && noerror) { // set keep-alive timeout (defaults to 5 minutes) hc.setReadTimeout(1000 * 60 * 5); // clear request and response header fields reqHeader->clearFields(); resHeader->clearFields(); resHeader->clearStatus(); } } // clean up request and response delete request; delete response; // monitor connection if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterServicingConnection(&hc); } // close connection hc.close(); }