Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose to internals #5333

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/

package io.vertx.core.http.impl;
package io.vertx.core.http;

import java.util.HashMap;
import java.util.Map;
Expand All @@ -20,7 +20,7 @@
*
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class MimeMapping {
public final class MimeMapping {
private static final Map<String, String> m = new HashMap<>();

static {
Expand Down Expand Up @@ -1013,14 +1013,23 @@ public class MimeMapping {
m.put("ice", "x-conference/x-cooltalk");
}

public static String getMimeTypeForExtension(String ext) {
/**
* @param ext the file name extension
* @return the matching mime type for a file extension (e.g. {@code .mkv}) or {@code null}
*/
public static String mimeTypeForExtension(String ext) {
return m.get(ext);
}
public static String getMimeTypeForFilename(String filename) {

/**
* @param filename the file name
* @return the matching mime type for a file name or {@code null}, the file extension is used for lookup
*/
public static String mimeTypeForFilename(String filename) {
int li = filename.lastIndexOf('.');
if (li != -1 && li != filename.length() - 1) {
String ext = filename.substring(li + 1, filename.length());
return MimeMapping.getMimeTypeForExtension(ext);
String ext = filename.substring(li + 1);
return mimeTypeForExtension(ext);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,8 @@
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.internal.buffer.BufferInternal;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpClosedException;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.impl.headers.HeadersMultiMap;
import io.vertx.core.internal.ContextInternal;
import io.vertx.core.internal.VertxInternal;
Expand Down Expand Up @@ -459,7 +455,7 @@ public Future<Void> sendFile(String filename, long offset, long length) {
}

if (!headers.contains(HttpHeaders.CONTENT_TYPE)) {
String contentType = MimeMapping.getMimeTypeForFilename(filename);
String contentType = MimeMapping.mimeTypeForFilename(filename);
if (contentType != null) {
headers.set(HttpHeaders.CONTENT_TYPE, contentType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,8 @@
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.*;
import io.vertx.core.internal.buffer.BufferInternal;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.StreamPriority;
import io.vertx.core.http.StreamResetException;
import io.vertx.core.http.impl.headers.Http2HeadersAdaptor;
import io.vertx.core.internal.PromiseInternal;
import io.vertx.core.net.HostAndPort;
Expand Down Expand Up @@ -559,7 +554,7 @@ public Future<Void> sendFile(String filename, long offset, long length) {
putHeader(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(contentLength));
}
if (headers.get(HttpHeaderNames.CONTENT_TYPE) == null) {
String contentType = MimeMapping.getMimeTypeForFilename(filename);
String contentType = MimeMapping.mimeTypeForFilename(filename);
if (contentType != null) {
putHeader(HttpHeaderNames.CONTENT_TYPE, contentType);
}
Expand Down
107 changes: 0 additions & 107 deletions vertx-core/src/main/java/io/vertx/core/http/impl/HttpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,113 +197,6 @@ public StreamPriority setExclusive(boolean exclusive) {
private HttpUtils() {
}

/**
* Normalizes a path as per <a href="http://tools.ietf.org/html/rfc3986#section-5.2.4>rfc3986</a>.
*
* There are 2 extra transformations that are not part of the spec but kept for backwards compatibility:
*
* double slash // will be converted to single slash and the path will always start with slash.
*
* Null paths are not normalized as nothing can be said about them.
*
* @param pathname raw path
* @return normalized path
*/
public static String normalizePath(String pathname) {
if (pathname == null) {
return null;
}

// add trailing slash if not set
if (pathname.isEmpty()) {
return "/";
}

int indexOfFirstPercent = pathname.indexOf('%');
if (indexOfFirstPercent == -1) {
// no need to removeDots nor replace double slashes
if (pathname.indexOf('.') == -1 && pathname.indexOf("//") == -1) {
if (pathname.charAt(0) == '/') {
return pathname;
}
// See https://bugs.openjdk.org/browse/JDK-8085796
return "/" + pathname;
}
}
return normalizePathSlow(pathname, indexOfFirstPercent);
}

private static String normalizePathSlow(String pathname, int indexOfFirstPercent) {
final StringBuilder ibuf;
// Not standard!!!
if (pathname.charAt(0) != '/') {
ibuf = new StringBuilder(pathname.length() + 1);
ibuf.append('/');
if (indexOfFirstPercent != -1) {
indexOfFirstPercent++;
}
} else {
ibuf = new StringBuilder(pathname.length());
}
ibuf.append(pathname);
if (indexOfFirstPercent != -1) {
decodeUnreservedChars(ibuf, indexOfFirstPercent);
}
// remove dots as described in
// http://tools.ietf.org/html/rfc3986#section-5.2.4
return RFC3986.removeDotSegments(ibuf);
}

private static void decodeUnreservedChars(StringBuilder path, int start) {
while (start < path.length()) {
// decode unreserved chars described in
// http://tools.ietf.org/html/rfc3986#section-2.4
if (path.charAt(start) == '%') {
decodeUnreserved(path, start);
}

start++;
}
}

private static void decodeUnreserved(StringBuilder path, int start) {
if (start + 3 <= path.length()) {
// these are latin chars so there is no danger of falling into some special unicode char that requires more
// than 1 byte
final String escapeSequence = path.substring(start + 1, start + 3);
int unescaped;
try {
unescaped = Integer.parseInt(escapeSequence, 16);
if (unescaped < 0) {
throw new IllegalArgumentException("Invalid escape sequence: %" + escapeSequence);
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid escape sequence: %" + escapeSequence);
}
// validate if the octet is within the allowed ranges
if (
// ALPHA
(unescaped >= 0x41 && unescaped <= 0x5A) ||
(unescaped >= 0x61 && unescaped <= 0x7A) ||
// DIGIT
(unescaped >= 0x30 && unescaped <= 0x39) ||
// HYPHEN
(unescaped == 0x2D) ||
// PERIOD
(unescaped == 0x2E) ||
// UNDERSCORE
(unescaped == 0x5F) ||
// TILDE
(unescaped == 0x7E)) {

path.setCharAt(start, (char) unescaped);
path.delete(start + 1, start + 3);
}
} else {
throw new IllegalArgumentException("Invalid position for escape character: " + start);
}
}

/**
* Resolve an URI reference as per <a href="http://tools.ietf.org/html/rfc3986#section-5.2.4>rfc3986</a>
*/
Expand Down
103 changes: 103 additions & 0 deletions vertx-core/src/main/java/io/vertx/core/internal/net/RFC3986.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,107 @@ private static int indexOfSlash(CharSequence str, int start) {
}
return -1;
}

/**
* Normalizes a path as per <a href="http://tools.ietf.org/html/rfc3986#section-5.2.4>rfc3986</a>.
*
* There are 2 extra transformations that are not part of the spec but kept for backwards compatibility:
*
* double slash // will be converted to single slash and the path will always start with slash.
*
* Null paths are not normalized as nothing can be said about them.
*
* @param pathname raw path
* @return normalized path
*/
public static String normalizePath(String pathname) {
// add trailing slash if not set
if (pathname.isEmpty()) {
return "/";
}

int indexOfFirstPercent = pathname.indexOf('%');
if (indexOfFirstPercent == -1) {
// no need to removeDots nor replace double slashes
if (pathname.indexOf('.') == -1 && pathname.indexOf("//") == -1) {
if (pathname.charAt(0) == '/') {
return pathname;
}
// See https://bugs.openjdk.org/browse/JDK-8085796
return "/" + pathname;
}
}
return normalizePathSlow(pathname, indexOfFirstPercent);
}

private static String normalizePathSlow(String pathname, int indexOfFirstPercent) {
final StringBuilder ibuf;
// Not standard!!!
if (pathname.charAt(0) != '/') {
ibuf = new StringBuilder(pathname.length() + 1);
ibuf.append('/');
if (indexOfFirstPercent != -1) {
indexOfFirstPercent++;
}
} else {
ibuf = new StringBuilder(pathname.length());
}
ibuf.append(pathname);
if (indexOfFirstPercent != -1) {
decodeUnreservedChars(ibuf, indexOfFirstPercent);
}
// remove dots as described in
// http://tools.ietf.org/html/rfc3986#section-5.2.4
return RFC3986.removeDotSegments(ibuf);
}

private static void decodeUnreservedChars(StringBuilder path, int start) {
while (start < path.length()) {
// decode unreserved chars described in
// http://tools.ietf.org/html/rfc3986#section-2.4
if (path.charAt(start) == '%') {
decodeUnreserved(path, start);
}

start++;
}
}

private static void decodeUnreserved(StringBuilder path, int start) {
if (start + 3 <= path.length()) {
// these are latin chars so there is no danger of falling into some special unicode char that requires more
// than 1 byte
final String escapeSequence = path.substring(start + 1, start + 3);
int unescaped;
try {
unescaped = Integer.parseInt(escapeSequence, 16);
if (unescaped < 0) {
throw new IllegalArgumentException("Invalid escape sequence: %" + escapeSequence);
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid escape sequence: %" + escapeSequence);
}
// validate if the octet is within the allowed ranges
if (
// ALPHA
(unescaped >= 0x41 && unescaped <= 0x5A) ||
(unescaped >= 0x61 && unescaped <= 0x7A) ||
// DIGIT
(unescaped >= 0x30 && unescaped <= 0x39) ||
// HYPHEN
(unescaped == 0x2D) ||
// PERIOD
(unescaped == 0x2E) ||
// UNDERSCORE
(unescaped == 0x5F) ||
// TILDE
(unescaped == 0x7E)) {

path.setCharAt(start, (char) unescaped);
path.delete(start + 1, start + 3);
}
} else {
throw new IllegalArgumentException("Invalid position for escape character: " + start);
}
}
}
Loading
Loading