snoopdave commented on code in PR #161:
URL: https://github.com/apache/roller/pull/161#discussion_r4238359882


##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -63,102 +55,110 @@
 
     private static final String ATOM_CONTENT_TYPE = "application/atom+xml";
 
-    /**
-     * Request attribute that carries the handler authenticated by this servlet
-     * to {@link RollerAtomHandlerFactory}, so Propono does not authenticate 
the
-     * request a second time.
-     */
-    static final String HANDLER_ATTRIBUTE = RollerAtomServlet.class.getName() 
+ ".handler";
-
     @Override
-    protected void service(HttpServletRequest req, HttpServletResponse res)
-            throws ServletException, IOException {
+    protected void service(HttpServletRequest request, HttpServletResponse 
response)
+            throws IOException {
 
         if 
(!WebloggerRuntimeConfig.getBooleanProperty("webservices.enableAtomPub")) {
-            LOG.debug("AtomPub service is disabled; rejecting request");
-            sendText(res, HttpServletResponse.SC_NOT_FOUND, "AtomPub service 
is disabled");
+            log.debug("AtomPub service is disabled; rejecting request");
+            sendText(response, HttpServletResponse.SC_NOT_FOUND, "AtomPub 
service is disabled");
             return;
         }
 
-        if (!carriesEntry(req)) {
-            forward(req, res);
+        String method = request.getMethod();
+        if (!"GET".equals(method) && !"POST".equals(method)
+                && !"PUT".equals(method) && !"DELETE".equals(method)) {
+            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
             return;
         }
 
-        // Authenticate before reading the body, as Propono does.
-        AtomHandler handler = createHandler(req, res);
-        if (handler.getAuthenticatedUsername() == null) {
-            res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
-            res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        // Authenticate before reading the body.
+        RollerAtomHandler handler = createHandler(request, response);
+        String userName = handler.getAuthenticatedUsername();
+        if (userName == null) {
+            // The OAuth path may have already written a challenge/error 
response.
+            if (!response.isCommitted()) {
+                response.setHeader("WWW-Authenticate", "Basic 
realm=\"Roller\"");
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            }
             return;
         }
-        req.setAttribute(HANDLER_ATTRIBUTE, handler);
 
-        int maxEntryBytes = maxEntryBytes();
-        // Read one byte past the limit, so an oversized body can be detected.
-        byte[] body = req.getInputStream().readNBytes(maxEntryBytes + 1);
-        if (body.length > maxEntryBytes) {
-            sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, 
"Entry is too large");
+        if ("POST".equals(method) && request.getContentType() == null) {
+            sendText(response, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
+                    "No content-type specified in request");
             return;
         }
-        DefaultHandler contentHandler = new DefaultHandler() {
-            @Override
-            public void error(SAXParseException e) throws SAXException {
-                throw e;
+
+        AtomRequest areq;
+        AtomEntry entry = null;
+        if (carriesEntry(request)) {
+            int maxEntryBytes = maxEntryBytes();
+            // Read one byte past the limit, so an oversized body can be 
detected.
+            byte[] body = request.getInputStream().readNBytes(maxEntryBytes + 
1);
+            if (body.length > maxEntryBytes) {
+                sendText(response, 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
+                        "Entry is too large");
+                return;
             }
-        };
-        XMLReader reader;
-        try {
-            reader = 
SecureXmlParsers.newSAXParserFactory().newSAXParser().getXMLReader();
-            // Hardening: DOCTYPE declarations should be rejected,
-            // regardless whether the secure reader already does it.
-            DefaultHandler2 doctypeRefuser = new DefaultHandler2() {
-                @Override
-                public void startDTD(String name, String publicId, String 
systemId)
-                        throws SAXException {
-                    throw new SAXException("DOCTYPE is not allowed in an Atom 
entry");
-                }
-            };
-            
reader.setProperty("http://xml.org/sax/properties/lexical-handler";, 
doctypeRefuser);
-        } catch (ParserConfigurationException | SAXException e) {
-            throw new ServletException("Could not create an Atom entry 
parser", e);
+            try {
+                entry = new AtomReader().parseEntry(new 
ByteArrayInputStream(body));
+            } catch (AtomException e) {
+                log.debug("Rejecting Atom entry that could not be parsed", e);
+                sendText(response, HttpServletResponse.SC_BAD_REQUEST, 
"Invalid Atom entry");
+                return;
+            }
+            areq = new AtomRequest(request, body);
+        } else {
+            // Media bodies are streamed to a temporary file by 
MediaCollection,
+            // where the upload size and quota are checked.
+            areq = AtomRequest.streaming(request);
         }
-        reader.setContentHandler(contentHandler);
-        reader.setErrorHandler(contentHandler);
+
         try {
-            // Propono reads the entry as UTF-8 text, so check the same text.
-            reader.parse(new InputSource(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8)));
-        } catch (SAXException e) {
-            LOG.debug("Rejecting Atom entry that could not be parsed", e);
-            sendText(res, HttpServletResponse.SC_BAD_REQUEST, "Invalid Atom 
entry");
-            return;
+            switch (method) {
+                case "GET":
+                    doGet(handler, areq, response);
+                    break;
+                case "POST":
+                    doPost(handler, areq, entry, response);
+                    break;
+                case "PUT":
+                    doPut(handler, areq, entry, response);
+                    break;
+                default:
+                    handler.deleteEntry(areq);
+                    response.setStatus(HttpServletResponse.SC_OK);
+            }
+        } catch (AtomException ae) {
+            log.debug("Returning error to client: " + ae.getMessage(), ae);
+            if (!response.isCommitted()) {
+                response.sendError(ae.getStatus(), ae.getMessage());

Review Comment:
   🤖Claude: fixed by sending only the status code for 5xx AtomExceptions and 
logging the details server-side. 4xx responses keep their message, which Roller 
writes for the client (for example "Cannot find specified resource").



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -63,102 +55,110 @@
 
     private static final String ATOM_CONTENT_TYPE = "application/atom+xml";
 
-    /**
-     * Request attribute that carries the handler authenticated by this servlet
-     * to {@link RollerAtomHandlerFactory}, so Propono does not authenticate 
the
-     * request a second time.
-     */
-    static final String HANDLER_ATTRIBUTE = RollerAtomServlet.class.getName() 
+ ".handler";
-
     @Override
-    protected void service(HttpServletRequest req, HttpServletResponse res)
-            throws ServletException, IOException {
+    protected void service(HttpServletRequest request, HttpServletResponse 
response)
+            throws IOException {
 
         if 
(!WebloggerRuntimeConfig.getBooleanProperty("webservices.enableAtomPub")) {
-            LOG.debug("AtomPub service is disabled; rejecting request");
-            sendText(res, HttpServletResponse.SC_NOT_FOUND, "AtomPub service 
is disabled");
+            log.debug("AtomPub service is disabled; rejecting request");
+            sendText(response, HttpServletResponse.SC_NOT_FOUND, "AtomPub 
service is disabled");
             return;
         }
 
-        if (!carriesEntry(req)) {
-            forward(req, res);
+        String method = request.getMethod();
+        if (!"GET".equals(method) && !"POST".equals(method)
+                && !"PUT".equals(method) && !"DELETE".equals(method)) {
+            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
             return;
         }
 
-        // Authenticate before reading the body, as Propono does.
-        AtomHandler handler = createHandler(req, res);
-        if (handler.getAuthenticatedUsername() == null) {
-            res.setHeader("WWW-Authenticate", "BASIC realm=\"AtomPub\"");
-            res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+        // Authenticate before reading the body.
+        RollerAtomHandler handler = createHandler(request, response);
+        String userName = handler.getAuthenticatedUsername();
+        if (userName == null) {
+            // The OAuth path may have already written a challenge/error 
response.
+            if (!response.isCommitted()) {
+                response.setHeader("WWW-Authenticate", "Basic 
realm=\"Roller\"");
+                response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+            }
             return;
         }
-        req.setAttribute(HANDLER_ATTRIBUTE, handler);
 
-        int maxEntryBytes = maxEntryBytes();
-        // Read one byte past the limit, so an oversized body can be detected.
-        byte[] body = req.getInputStream().readNBytes(maxEntryBytes + 1);
-        if (body.length > maxEntryBytes) {
-            sendText(res, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, 
"Entry is too large");
+        if ("POST".equals(method) && request.getContentType() == null) {
+            sendText(response, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE,
+                    "No content-type specified in request");
             return;
         }
-        DefaultHandler contentHandler = new DefaultHandler() {
-            @Override
-            public void error(SAXParseException e) throws SAXException {
-                throw e;
+
+        AtomRequest areq;
+        AtomEntry entry = null;
+        if (carriesEntry(request)) {
+            int maxEntryBytes = maxEntryBytes();
+            // Read one byte past the limit, so an oversized body can be 
detected.
+            byte[] body = request.getInputStream().readNBytes(maxEntryBytes + 
1);
+            if (body.length > maxEntryBytes) {
+                sendText(response, 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE,
+                        "Entry is too large");
+                return;
             }
-        };
-        XMLReader reader;
-        try {
-            reader = 
SecureXmlParsers.newSAXParserFactory().newSAXParser().getXMLReader();
-            // Hardening: DOCTYPE declarations should be rejected,
-            // regardless whether the secure reader already does it.
-            DefaultHandler2 doctypeRefuser = new DefaultHandler2() {
-                @Override
-                public void startDTD(String name, String publicId, String 
systemId)
-                        throws SAXException {
-                    throw new SAXException("DOCTYPE is not allowed in an Atom 
entry");
-                }
-            };
-            
reader.setProperty("http://xml.org/sax/properties/lexical-handler";, 
doctypeRefuser);
-        } catch (ParserConfigurationException | SAXException e) {
-            throw new ServletException("Could not create an Atom entry 
parser", e);
+            try {
+                entry = new AtomReader().parseEntry(new 
ByteArrayInputStream(body));
+            } catch (AtomException e) {
+                log.debug("Rejecting Atom entry that could not be parsed", e);
+                sendText(response, HttpServletResponse.SC_BAD_REQUEST, 
"Invalid Atom entry");
+                return;
+            }
+            areq = new AtomRequest(request, body);
+        } else {
+            // Media bodies are streamed to a temporary file by 
MediaCollection,
+            // where the upload size and quota are checked.
+            areq = AtomRequest.streaming(request);
         }
-        reader.setContentHandler(contentHandler);
-        reader.setErrorHandler(contentHandler);
+
         try {
-            // Propono reads the entry as UTF-8 text, so check the same text.
-            reader.parse(new InputSource(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8)));
-        } catch (SAXException e) {
-            LOG.debug("Rejecting Atom entry that could not be parsed", e);
-            sendText(res, HttpServletResponse.SC_BAD_REQUEST, "Invalid Atom 
entry");
-            return;
+            switch (method) {
+                case "GET":
+                    doGet(handler, areq, response);
+                    break;
+                case "POST":
+                    doPost(handler, areq, entry, response);
+                    break;
+                case "PUT":
+                    doPut(handler, areq, entry, response);
+                    break;
+                default:
+                    handler.deleteEntry(areq);
+                    response.setStatus(HttpServletResponse.SC_OK);
+            }
+        } catch (AtomException ae) {
+            log.debug("Returning error to client: " + ae.getMessage(), ae);
+            if (!response.isCommitted()) {
+                response.sendError(ae.getStatus(), ae.getMessage());
+            }
+        } catch (Exception e) {
+            log.error("Unexpected error handling AtomPub request", e);
+            if (!response.isCommitted()) {
+                
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
e.getMessage());

Review Comment:
   🤖Claude: fixed by no longer sending e.getMessage() for unexpected 
exceptions; the client gets a bare 500 and the exception is logged. Test: 
serverErrorsDoNotExposeTheExceptionMessage.



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +188,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(response, created);
+    }
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
+
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(HttpServletResponse response, AtomEntry entry)
+            throws AtomException {
+        String editHref = entry.getLinkHref("edit");
+        if (editHref != null) {
+            response.setHeader("Location", editHref);

Review Comment:
   🤖Claude: fixed by adding RollerAtomServlet.safeLocation(): 
Location/Content-Location are set only when the edit URI starts with this 
server's AtomPub URL (from the URL strategy) and parses as a URI; otherwise the 
header is omitted. Tests: locationMustBeAValidUriUnderTheAtomUrl, 
createdEntryLocationIsSetOnlyWhenSafe.



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +188,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(response, created);
+    }
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
+
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(HttpServletResponse response, AtomEntry entry)
+            throws AtomException {
+        String editHref = entry.getLinkHref("edit");
+        if (editHref != null) {
+            response.setHeader("Location", editHref);

Review Comment:
   🤖Claude: fixed by building the header value with new 
URI(href).toASCIIString(), which rejects CR/LF and other illegal characters and 
percent-encodes non-ASCII. Unsafe values leave the header unset.



##########
app/src/main/java/org/apache/roller/weblogger/webservices/atomprotocol/RollerAtomServlet.java:
##########
@@ -188,61 +188,94 @@
         res.getWriter().write(message);
     }
 
-    /** A request whose body has already been read into memory. */
-    static final class BufferedBodyRequest extends HttpServletRequestWrapper {
+    private void doGet(RollerAtomHandler handler, AtomRequest areq, 
HttpServletResponse response)
+            throws AtomException, IOException {
 
-        private final byte[] body;
+        if (handler.isAtomServiceURI(areq)) {
+            AtomServiceDoc service = handler.getAtomService(areq);
+            response.setContentType(AtomConstants.SERVICE_MEDIA_TYPE);
+            new AtomWriter().writeServiceDoc(response.getOutputStream(), 
service);
 
-        BufferedBodyRequest(HttpServletRequest request, byte[] body) {
-            super(request);
-            this.body = body;
-        }
+        } else if (handler.isCollectionURI(areq)) {
+            AtomFeed feed = handler.getCollection(areq);
+            response.setContentType(AtomConstants.FEED_MEDIA_TYPE);
+            new AtomWriter().writeFeed(response.getOutputStream(), feed);
 
-        @Override
-        public ServletInputStream getInputStream() {
-            final ByteArrayInputStream in = new ByteArrayInputStream(body);
-            return new ServletInputStream() {
-                @Override
-                public int read() {
-                    return in.read();
-                }
+        } else if (handler.isEntryURI(areq)) {
+            AtomEntry entry = handler.getEntry(areq);
+            response.setContentType(AtomConstants.ENTRY_MEDIA_TYPE);
+            new AtomWriter().writeEntry(response.getOutputStream(), entry);
 
-                @Override
-                public int read(byte[] b, int off, int len) {
-                    return in.read(b, off, len);
-                }
+        } else if (handler.isMediaEditURI(areq)) {
+            AtomMediaResource resource = handler.getMediaResource(areq);
+            if (resource.getContentType() != null) {
+                response.setContentType(resource.getContentType());
+            }
+            response.setContentLengthLong(resource.getContentLength());
+            if (resource.getLastModified() != null) {
+                response.setDateHeader("Last-Modified", 
resource.getLastModified().getTime());
+            }
+            try (InputStream in = resource.getInputStream()) {
+                in.transferTo(response.getOutputStream());
+            }
 
-                @Override
-                public boolean isFinished() {
-                    return in.available() == 0;
-                }
+        } else {
+            throw new AtomNotFoundException("Cannot find specified resource");
+        }
+    }
 
-                @Override
-                public boolean isReady() {
-                    return true;
-                }
+    private void doPost(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
 
-                @Override
-                public void setReadListener(ReadListener listener) {
-                    throw new UnsupportedOperationException();
-                }
-            };
+        if (!handler.isCollectionURI(areq)) {
+            throw new AtomNotFoundException("Cannot POST to specified URI");
         }
 
-        @Override
-        public BufferedReader getReader() {
-            return new BufferedReader(new InputStreamReader(
-                    new ByteArrayInputStream(body), StandardCharsets.UTF_8));
+        String contentType = areq.getContentType();
+        AtomEntry created;
+        if (entry != null) {
+            created = handler.postEntry(areq, entry);
+        } else {
+            // Media POST: synthesize an entry carrying the request content 
type
+            // and Slug; the binary data is read from the request body.
+            AtomEntry mediaEntry = new AtomEntry();
+            AtomContent content = new AtomContent();
+            content.setType(contentType);
+            mediaEntry.setContent(content);
+            mediaEntry.setTitle(areq.getHeader("Slug"));
+            created = handler.postMedia(areq, mediaEntry);
         }
+        writeCreated(response, created);
+    }
 
-        @Override
-        public int getContentLength() {
-            return body.length;
+    private void doPut(RollerAtomHandler handler, AtomRequest areq, AtomEntry 
entry,
+            HttpServletResponse response) throws AtomException {
+
+        if (entry != null) {
+            handler.putEntry(areq, entry);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else if (handler.isMediaEditURI(areq)) {
+            handler.putMedia(areq);
+            response.setStatus(HttpServletResponse.SC_OK);
+        } else {
+            throw new AtomNotFoundException("Cannot PUT to specified URI");
         }
+    }
 
-        @Override
-        public long getContentLengthLong() {
-            return body.length;
+    private void writeCreated(HttpServletResponse response, AtomEntry entry)
+            throws AtomException {
+        String editHref = entry.getLinkHref("edit");
+        if (editHref != null) {
+            response.setHeader("Location", editHref);
+            response.setHeader("Content-Location", editHref);

Review Comment:
   🤖Claude: fixed by the same safeLocation() check as Location on the line 
above; Content-Location uses the validated value.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to