protected void respondWithToken(LockToken tok, Request request, Response response) { response.setStatus(Status.SC_OK); ByteArrayOutputStream out = new ByteArrayOutputStream(); XmlWriter writer = new XmlWriter(out); writer.writeXMLHeader(); String d = WebDavProtocol.DAV_PREFIX; writer.open(d + ":prop xmlns:" + d + "=\"DAV:\""); writer.newLine(); writer.open(d + ":lockdiscovery"); writer.newLine(); writer.open(d + ":activelock"); writer.newLine(); lockWriterHelper.appendType(writer, tok.info.type); lockWriterHelper.appendScope(writer, tok.info.scope); lockWriterHelper.appendDepth(writer, tok.info.depth); lockWriterHelper.appendOwner(writer, tok.info.lockedByUser); lockWriterHelper.appendTimeout(writer, tok.timeout.getSeconds()); lockWriterHelper.appendTokenId(writer, tok.tokenId); String url = PropFindPropertyBuilder.fixUrlForWindows(request.getAbsoluteUrl()); lockWriterHelper.appendRoot(writer, url); writer.close(d + ":activelock"); writer.close(d + ":lockdiscovery"); writer.close(d + ":prop"); writer.flush(); LogUtils.debug(log, "lock response: ", out); response.setEntity(new ByteArrayEntity(out.toByteArray())); // response.close(); }
@Override public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource) throws NotAuthorizedException, BadRequestException, ConflictException { try { org.jdom.input.SAXBuilder builder = new org.jdom.input.SAXBuilder(); // Prevent possibily of malicious clients using remote the parser to load remote resources builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); org.jdom.Document doc = builder.build(request.getInputStream()); String reportName = doc.getRootElement().getName(); Report r = reports.get(reportName); if (r == null) { log.error("report not known: " + reportName); throw new BadRequestException(resource); } else { log.info("process report: " + reportName + " with : " + r.getClass()); String xml = r.process(request.getHostHeader(), request.getAbsolutePath(), resource, doc); if (log.isTraceEnabled()) { log.trace("Report XML:\n" + xml); } response.setStatus(Response.Status.SC_MULTI_STATUS); response.setContentTypeHeader("text/xml"); response.setEntity(new ByteArrayEntity(xml.getBytes("UTF-8"))); } } catch (JDOMException ex) { java.util.logging.Logger.getLogger(ReportHandler.class.getName()).log(Level.SEVERE, null, ex); } catch (ReadingException ex) { throw new RuntimeException(ex); } catch (WritingException ex) { throw new RuntimeException(ex); } catch (IOException ex) { throw new RuntimeException(ex); } }
@Override public void respondWithOptions( Resource resource, Response response, Request request, List<String> methodsAllowed) { setRespondCommonHeaders(response, resource, Status.SC_OK, request.getAuthorization()); response.setAllowHeader(methodsAllowed); response.setContentLengthHeader((long) 0); }
@Override public void respondHead(Resource resource, Response response, Request request) { // setRespondContentCommonHeaders(response, resource, Response.Status.SC_NO_CONTENT, // request.getAuthorization()); setRespondContentCommonHeaders( response, resource, Response.Status.SC_OK, request.getAuthorization()); if (!(resource instanceof GetableResource)) { return; } GetableResource gr = (GetableResource) resource; Long contentLength = gr.getContentLength(); if (contentLength != null) { response.setContentLengthHeader(contentLength); } else { log.trace("No content length is available for HEAD request"); } String acc = request.getAcceptHeader(); String ct = gr.getContentType(acc); if (ct != null) { ct = pickBestContentType(ct); if (ct != null) { response.setContentTypeHeader(ct); } } }
protected void setRespondCommonHeaders( Response response, Resource resource, Response.Status status, Auth auth) { response.setStatus(status); response.setNonStandardHeader("Server", "milton.io-" + miltonVerson); response.setDateHeader(new Date()); response.setNonStandardHeader("Accept-Ranges", "bytes"); String etag = eTagGenerator.generateEtag(resource); if (etag != null) { response.setEtag(etag); } }
@Override public void respondUnauthorised(Resource resource, Response response, Request request) { if (authenticationService.canUseExternalAuth(resource, request)) { log.info("respondUnauthorised: use external authentication"); initiateExternalAuth(resource, request, response); } else { log.info("respondUnauthorised: return staus: " + Response.Status.SC_UNAUTHORIZED); response.setStatus(Response.Status.SC_UNAUTHORIZED); List<String> challenges = authenticationService.getChallenges(resource, request); response.setAuthenticateHeader(challenges); } }
@Override public void respondContent( Resource resource, Response response, Request request, Map<String, String> params) throws NotAuthorizedException, BadRequestException, NotFoundException { log.debug("respondContent: " + resource.getClass()); Auth auth = request.getAuthorization(); setRespondContentCommonHeaders(response, resource, auth); if (resource instanceof GetableResource) { GetableResource gr = (GetableResource) resource; String acc = request.getAcceptHeader(); String ct = gr.getContentType(acc); if (ct != null) { ct = pickBestContentType(ct); response.setContentTypeHeader(ct); } cacheControlHelper.setCacheControl(gr, response, request.getAuthorization()); Long contentLength = gr.getContentLength(); Boolean doBuffering = null; if (resource instanceof BufferingControlResource) { BufferingControlResource bcr = (BufferingControlResource) resource; doBuffering = bcr.isBufferingRequired(); } if (doBuffering == null) { if (buffering == null || buffering == BUFFERING.whenNeeded) { doBuffering = (contentLength == null); // if no content length then we buffer content to find content length } else { doBuffering = (buffering == BUFFERING .always); // if not null or whenNeeded then buffering is explicitly enabled or // disabled } } if (!doBuffering) { log.trace("sending content with known content length: " + contentLength); if (contentLength != null) { response.setContentLengthHeader(contentLength); } response.setEntity(new GetableResourceEntity(gr, params, ct)); } else { BufferingGetableResourceEntity e = new BufferingGetableResourceEntity(gr, params, ct, contentLength, getMaxMemorySize()); response.setEntity(e); } } }
/** * @param resource * @param response * @param message - optional message to output in the body content */ @Override public void respondConflict( Resource resource, Response response, Request request, String message) { log.debug("respondConflict"); response.setStatus(Response.Status.SC_CONFLICT); contentGenerator.generate(resource, request, response, Status.SC_CONFLICT); }
@Override public void respondNotModified(GetableResource resource, Response response, Request request) { log.trace("respondNotModified"); response.setStatus(Response.Status.SC_NOT_MODIFIED); response.setDateHeader(new Date()); String etag = eTagGenerator.generateEtag(resource); if (etag != null) { response.setEtag(etag); } // Note that we use a simpler modified date handling here then when // responding with content, because in a not-modified situation the // modified date MUST be that of the actual resource Date modDate = resource.getModifiedDate(); response.setLastModifiedHeader(modDate); cacheControlHelper.setCacheControl(resource, response, request.getAuthorization()); }
@Override public void respondMethodNotAllowed(Resource res, Response response, Request request) { log.debug( "method not allowed. handler: " + this.getClass().getName() + " resource: " + res.getClass().getName()); response.setStatus(Response.Status.SC_METHOD_NOT_ALLOWED); contentGenerator.generate(res, request, response, Status.SC_METHOD_NOT_ALLOWED); }
@Override public void respondRedirect(Response response, Request request, String redirectUrl) { if (redirectUrl == null) { throw new NullPointerException("redirectUrl cannot be null"); } log.trace("respondRedirect"); // delegate to the response, because this can be server dependent response.sendRedirect(redirectUrl); // response.setStatus(Response.Status.SC_MOVED_TEMPORARILY); // response.setLocationHeader(redirectUrl); }
private void processCreateAndLock( HttpManager manager, Request request, Response response, Resource parentResource, String name) throws NotAuthorizedException { if (parentResource instanceof LockingCollectionResource) { log.debug("parent supports lock-null. doing createAndLock"); LockingCollectionResource lockingParent = (LockingCollectionResource) parentResource; LockTimeout timeout = LockTimeout.parseTimeout(request); response.setContentTypeHeader(Response.XML); LockInfo lockInfo; try { lockInfo = LockInfoSaxHandler.parseLockInfo(request); } catch (SAXException ex) { throw new RuntimeException("Exception reading request body", ex); } catch (IOException ex) { throw new RuntimeException("Exception reading request body", ex); } // TODO: this should be refactored to return a LockResult as for existing entities log.debug("Creating lock on unmapped resource: " + name); LockToken tok = lockingParent.createAndLock(name, timeout, lockInfo); if (tok == null) { throw new RuntimeException( "createAndLock returned null, from resource of type: " + lockingParent.getClass().getCanonicalName()); } response.setStatus(Status.SC_CREATED); response.setLockTokenHeader( "<opaquelocktoken:" + tok.tokenId + ">"); // spec says to set response header. See 8.10.1 respondWithToken(tok, request, response); } else { log.debug("parent does not support lock-null, respondong method not allowed"); responseHandler.respondMethodNotImplemented(parentResource, response, request); } }
/** * The modified date response header is used by the client for content caching. It seems obvious * that if we have a modified date on the resource we should set it. BUT, because of the * interaction with max-age we should always set it to the current date if we have max-age The * problem, is that if we find that a condition GET has an expired mod-date (based on maxAge) then * we want to respond with content (even if our mod-date hasnt changed. But if we use the actual * mod-date in that case, then the browser will continue to use the old mod-date, so will forever * more respond with content. So we send a mod-date of now to ensure that future requests will be * given a 304 not modified.* * * @param response * @param resource * @param auth */ public static void setModifiedDate(Response response, Resource resource, Auth auth) { Date modDate = resource.getModifiedDate(); if (modDate != null) { // HACH - see if this helps IE response.setLastModifiedHeader(modDate); // if (resource instanceof GetableResource) { // GetableResource gr = (GetableResource) resource; // Long maxAge = gr.getMaxAgeSeconds(auth); // if (maxAge != null && maxAge > 0) { // log.trace("setModifiedDate: has a modified date and a positive maxAge, // so adjust modDate"); // long tm = System.currentTimeMillis() - 60000; // modified 1 minute ago // modDate = new Date(tm); // have max-age, so use current date // } // } // response.setLastModifiedHeader(modDate); } }
protected void processNewLock( HttpManager milton, Request request, Response response, LockableResource r, LockTimeout timeout) throws NotAuthorizedException { LockInfo lockInfo; try { lockInfo = LockInfoSaxHandler.parseLockInfo(request); } catch (SAXException ex) { throw new RuntimeException("Exception reading request body", ex); } catch (IOException ex) { throw new RuntimeException("Exception reading request body", ex); } if (handlerHelper.isLockedOut(request, r)) { this.responseHandler.respondLocked(request, response, r); return; } log.debug("locking: " + r.getName()); LockResult result; try { result = r.lock(timeout, lockInfo); } catch (PreConditionFailedException ex) { responseHandler.respondPreconditionFailed(request, response, r); return; } catch (LockedException ex) { responseHandler.respondLocked(request, response, r); return; } if (result.isSuccessful()) { LockToken tok = result.getLockToken(); log.debug("..locked ok: " + tok.tokenId); response.setLockTokenHeader( "<opaquelocktoken:" + tok.tokenId + ">"); // spec says to set response header. See 8.10.1 respondWithToken(tok, request, response); } else { respondWithLockFailure(result, request, response); } }
@Override public void respondPartialContent( GetableResource resource, Response response, Request request, Map<String, String> params, Range range) throws NotAuthorizedException, BadRequestException, NotFoundException { log.debug("respondPartialContent: " + range.getStart() + " - " + range.getFinish()); response.setStatus(Response.Status.SC_PARTIAL_CONTENT); long st = range.getStart() == null ? 0 : range.getStart(); long fn; Long cl = resource.getContentLength(); if (range.getFinish() == null) { if (cl != null) { fn = cl.longValue() - 1; // position is one less then length } else { log.warn( "Couldnt calculate range end position because the resource is not reporting a content length, and no end position was requested by the client: " + resource.getName() + " - " + resource.getClass()); fn = -1; } } else { fn = range.getFinish(); } response.setContentRangeHeader(st, fn, cl); long contentLength = fn - st + 1; response.setContentLengthHeader(contentLength); response.setDateHeader(new Date()); String etag = eTagGenerator.generateEtag(resource); if (etag != null) { response.setEtag(etag); } String acc = request.getAcceptHeader(); String ct = resource.getContentType(acc); if (ct != null) { response.setContentTypeHeader(ct); } response.setEntity(new GetableResourceEntity(resource, range, params, ct)); }
/** * (from the spec) 7.4 Write Locks and Null Resources * * <p>It is possible to assert a write lock on a null resource in order to lock the name. * * <p>A write locked null resource, referred to as a lock-null resource, MUST respond with a 404 * (Not Found) or 405 (Method Not Allowed) to any HTTP/1.1 or DAV methods except for PUT, MKCOL, * OPTIONS, PROPFIND, LOCK, and UNLOCK. A lock-null resource MUST appear as a member of its parent * collection. Additionally the lock-null resource MUST have defined on it all mandatory DAV * properties. Most of these properties, such as all the get* properties, will have no value as a * lock-null resource does not support the GET method. Lock-Null resources MUST have defined * values for lockdiscovery and supportedlock properties. * * <p>Until a method such as PUT or MKCOL is successfully executed on the lock-null resource the * resource MUST stay in the lock-null state. However, once a PUT or MKCOL is successfully * executed on a lock-null resource the resource ceases to be in the lock-null state. * * <p>If the resource is unlocked, for any reason, without a PUT, MKCOL, or similar method having * been successfully executed upon it then the resource MUST return to the null state. * * @param manager * @param request * @param response * @param host * @param url */ private void processNonExistingResource( HttpManager manager, Request request, Response response, String host, String url) throws NotAuthorizedException, BadRequestException { String name; Path parentPath = Path.path(url); name = parentPath.getName(); parentPath = parentPath.getParent(); url = parentPath.toString(); Resource r = manager.getResourceFactory().getResource(host, url); if (r != null) { if (!handlerHelper.checkAuthorisation(manager, r, request)) { responseHandler.respondUnauthorised(r, response, request); return; } else { processCreateAndLock(manager, request, response, r, name); } } else { log.debug("couldnt find parent to execute lock-null, returning not found"); // respondNotFound(response,request); response.setStatus(Status.SC_CONFLICT); } }
protected void processExistingResource( HttpManager manager, Request request, Response response, Resource resource) throws NotAuthorizedException { if (handlerHelper.isNotCompatible(resource, request.getMethod()) || !isCompatible(resource)) { responseHandler.respondMethodNotImplemented(resource, response, request); return; } if (!handlerHelper.checkAuthorisation(manager, resource, request)) { responseHandler.respondUnauthorised(resource, response, request); return; } handlerHelper.checkExpects(responseHandler, request, response); LockableResource r = (LockableResource) resource; LockTimeout timeout = LockTimeout.parseTimeout(request); String ifHeader = request.getIfHeader(); response.setContentTypeHeader(Response.XML); if (ifHeader == null || ifHeader.length() == 0) { processNewLock(manager, request, response, r, timeout); } else { processRefresh(manager, request, response, r, timeout, ifHeader); } }
@Override public void respondNotFound(Response response, Request request) { response.setStatus(Response.Status.SC_NOT_FOUND); response.setContentTypeHeader("text/html"); contentGenerator.generate(null, request, response, Status.SC_NOT_FOUND); }
@Override public void respondPreconditionFailed(Request request, Response response, Resource resource) { response.setStatus(Status.SC_PRECONDITION_FAILED); }
@Override public void respondDeleteFailed( Request request, Response response, Resource resource, Status status) { response.setStatus(status); }
@Override public void respondForbidden(Resource resource, Response response, Request request) { response.setStatus(Response.Status.SC_FORBIDDEN); }
@Override public void respondBadRequest(Resource resource, Response response, Request request) { response.setStatus(Response.Status.SC_BAD_REQUEST); }
@Override public void respondMethodNotImplemented(Resource resource, Response response, Request request) { response.setStatus(Response.Status.SC_NOT_IMPLEMENTED); contentGenerator.generate(resource, request, response, Status.SC_NOT_IMPLEMENTED); }
@Override public void respondServerError(Request request, Response response, String reason) { response.setStatus(Status.SC_INTERNAL_SERVER_ERROR); contentGenerator.generate(null, request, response, Status.SC_INTERNAL_SERVER_ERROR); }
@Override public void respondExpectationFailed(Response response, Request request) { response.setStatus(Response.Status.SC_EXPECTATION_FAILED); }
private void respondWithLockFailure(LockResult result, Request request, Response response) { log.info("respondWithLockFailure: " + result.getFailureReason().name()); response.setStatus(result.getFailureReason().status); }
@Override public void sendContent( OutputStream out, Range range, Map<String, String> params, String contentType) throws IOException, NotAuthorizedException, BadRequestException, NotFoundException { MediaMetaData mmd = MediaMetaData.find(rPrimary.getHash(), SessionManager.session()); if (mmd != null) { Integer durationSecs = mmd.getDurationSecs(); if (durationSecs != null) { Response resp = HttpManager.response(); if (resp != null) { System.out.println("set duration header: " + durationSecs); resp.setNonStandardHeader("X-Content-Duration", durationSecs.toString()); } } } else { System.out.println("no metadata for: " + rPrimary.getHash()); } try { boolean force = params.containsKey("force"); if (altFormat == null || force) { // hack start if (params.containsKey("args")) { List<String> args = new ArrayList<>(); for (String s : params.get("args").split(",")) { args.add(s); } String[] arr = new String[args.size()]; args.toArray(arr); formatSpec.setConverterArgs(arr); System.out.println("set args: " + arr); } // hack end System.out.println("generate: " + getName()); GenerateJob j = altFormatGenerator.getOrEnqueueJob( rPrimary.getHash(), rPrimary.getName(), formatSpec); System.out.println("got job: " + j); // Wait until the file exists int cnt = 0; System.out.println("check if exists..."); while (!j.getDestFile().exists() && !j.done()) { cnt++; System.out.println( "sleep..." + cnt + " .. " + j.getDestFile().exists() + " - " + j.done()); doSleep(cnt++, 200, 70); } System.out.println("finished sleepy check"); if (!j.getDestFile().exists()) { throw new RuntimeException( "Job did not create a destination file: " + j.getDestFile().getAbsolutePath()); } System.out.println( "use dest file: " + j.getDestFile().getAbsolutePath() + " size: " + j.getDestFile().length()); FileInputStream fin = new FileInputStream(j.getDestFile()); byte[] buf = new byte[1024]; System.out.println("send file..."); // Read the file until the job is done, or we run out of bytes int s = fin.read(buf); System.out.println("send file... " + s); long bytes = 0; while (!j.done() || s > 0) { if (s < 0) { // no bytes available, but job is not done, so wait System.out.println("sleep..."); doSleep(100); } else { System.out.println("write bytes: " + s); bytes += s; out.write(buf, 0, s); } s = fin.read(buf); } System.out.println("finished sending file: " + bytes); } else { System.out.println("using pre-existing al-format"); Combiner combiner = new Combiner(); List<String> fanoutCrcs = getFanout().getHashes(); combiner.combine(fanoutCrcs, hashStore, blobStore, out); out.flush(); } } catch (Throwable e) { log.error("Exception sending content", e); throw new IOException("Exception sending content"); } }