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

8245095: Implementation of JEP 408: Simple Web Server #5505

Closed
wants to merge 24 commits into from
Closed
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
023356c
Move changes from sandbox to mainline
FrauBoes Sep 14, 2021
093263d
fix whitespace
FrauBoes Sep 14, 2021
2d3d293
remove UnmodifiableHeaders constant
FrauBoes Sep 14, 2021
a7e82ba
add module main class to symbolgenerator
lahodaj Sep 14, 2021
f53c59d
small spec rewording
FrauBoes Sep 15, 2021
3084130
correct path handling
FrauBoes Sep 15, 2021
7f1ceff
improve output on startup
FrauBoes Sep 20, 2021
b9f4019
Merge branch 'master' into componentcheck
FrauBoes Sep 20, 2021
2fbf838
add checks for all path segments
FrauBoes Sep 20, 2021
793042f
Merge branch 'master' into simpleserver
FrauBoes Sep 20, 2021
89b6994
check isHidden, isSymlink, isReadable for all path segments
FrauBoes Sep 20, 2021
1052329
Merge branch 'master' into simpleserver
FrauBoes Sep 20, 2021
3081087
Merge branch 'master' into simpleserver
FrauBoes Sep 21, 2021
4c8c47f
refactor isHidden,isReadable,isSymlink checks and cleanup tests
FrauBoes Sep 21, 2021
2ae3cb6
Merge remote-tracking branch 'origin/simpleserver' into simpleserver
FrauBoes Sep 21, 2021
fe05913
Merge branch 'master' into simpleserver
FrauBoes Sep 21, 2021
6631d8e
address PR comments
FrauBoes Sep 22, 2021
639e018
change default bind address from anylocal to loopback
FrauBoes Sep 22, 2021
0aafaa5
Merge branch 'master' into simpleserver
FrauBoes Sep 27, 2021
7f99447
use ipv4/ipv6 specific loopback address and add add how-to output for…
FrauBoes Sep 27, 2021
a7ce5b2
update output for all interfaces
FrauBoes Sep 29, 2021
1f0ed91
Merge branch 'master' into simpleserver
FrauBoes Sep 29, 2021
27d0a2b
Merge branch 'master' into simpleserver
FrauBoes Sep 29, 2021
e86609d
Minor rewording of bind address output
FrauBoes Oct 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
#
# Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -196,6 +196,11 @@ else # not java.base
endif
endif

# Set main class of jdk.httpserver module
ifeq ($(MODULE), jdk.httpserver)
JMOD_FLAGS += --main-class sun.net.httpserver.simpleserver.Main
endif

# Changes to the jmod tool itself should also trigger a rebuild of all jmods.
# The variable JMOD_CMD could contain an environment variable assignment before
# the actual command. Filter that out using wildcard before adding to DEPS.
@@ -123,6 +123,7 @@
import com.sun.tools.classfile.InnerClasses_attribute.Info;
import com.sun.tools.classfile.Method;
import com.sun.tools.classfile.MethodParameters_attribute;
import com.sun.tools.classfile.ModuleMainClass_attribute;
import com.sun.tools.classfile.ModuleResolution_attribute;
import com.sun.tools.classfile.ModuleTarget_attribute;
import com.sun.tools.classfile.Module_attribute;
@@ -928,6 +929,12 @@ private void addAttributes(ModuleDescription md,
attributes.put(Attribute.ModuleTarget,
new ModuleTarget_attribute(attrIdx, targetIdx));
}
if (header.moduleMainClass != null) {
int attrIdx = addString(cp, Attribute.ModuleMainClass);
int targetIdx = addString(cp, header.moduleMainClass);
attributes.put(Attribute.ModuleMainClass,
new ModuleMainClass_attribute(attrIdx, targetIdx));
}
int attrIdx = addString(cp, Attribute.Module);
attributes.put(Attribute.Module,
new Module_attribute(attrIdx,
@@ -2294,6 +2301,13 @@ private boolean readAttribute(ClassFile cf, FeatureDescription feature, Attribut
chd.isSealed = true;
break;
}
case Attribute.ModuleMainClass: {
ModuleMainClass_attribute moduleMainClass = (ModuleMainClass_attribute) attr;
assert feature instanceof ModuleHeaderDescription;
ModuleHeaderDescription mhd = (ModuleHeaderDescription) feature;
mhd.moduleMainClass = moduleMainClass.getMainClassName(cf.constant_pool);
break;
}
default:
throw new IllegalStateException("Unhandled attribute: " +
attrName);
@@ -2731,6 +2745,7 @@ static class ModuleHeaderDescription extends HeaderDescription {
List<ProvidesDescription> provides = new ArrayList<>();
Integer moduleResolution;
String moduleTarget;
String moduleMainClass;

@Override
public int hashCode() {
@@ -2743,6 +2758,7 @@ public int hashCode() {
hash = 83 * hash + Objects.hashCode(this.provides);
hash = 83 * hash + Objects.hashCode(this.moduleResolution);
hash = 83 * hash + Objects.hashCode(this.moduleTarget);
hash = 83 * hash + Objects.hashCode(this.moduleMainClass);
return hash;
}

@@ -2781,6 +2797,10 @@ public boolean equals(Object obj) {
other.moduleResolution)) {
return false;
}
if (!Objects.equals(this.moduleMainClass,
other.moduleMainClass)) {
return false;
}
return true;
}

@@ -2818,6 +2838,8 @@ public void write(Appendable output, String baselineVersion,
output.append(" resolution " +
quote(Integer.toHexString(moduleResolution),
true));
if (moduleMainClass != null)
output.append(" moduleMainClass " + quote(moduleMainClass, true));
writeAttributes(output);
output.append("\n");
writeInnerClasses(output, baselineVersion, version);
@@ -2862,6 +2884,8 @@ public boolean read(LineBasedReader reader) throws IOException {
moduleResolution = Integer.parseInt(resolutionFlags, 16);
}

moduleMainClass = reader.attributes.get("moduleMainClass");

readAttributes(reader);
reader.moveNext();
readInnerClasses(reader);
@@ -0,0 +1,41 @@
#
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#

include GensrcCommonJdk.gmk
include GensrcProperties.gmk
include Modules.gmk

################################################################################

# Use wildcard so as to avoid getting non-existing directories back
SIMPLESERVER_RESOURCES_DIRS := $(wildcard $(addsuffix /sun/net/httpserver/simpleserver/resources, \
$(call FindModuleSrcDirs, jdk.httpserver)))

$(eval $(call SetupCompileProperties, SIMPLESERVER_PROPERTIES, \
SRC_DIRS := $(SIMPLESERVER_RESOURCES_DIRS), \
CLASS := ListResourceBundle, \
))

TARGETS += $(SIMPLESERVER_PROPERTIES)
@@ -28,10 +28,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import sun.net.httpserver.DelegatingHttpExchange;

/**
* A filter used to pre- and post-process incoming requests. Pre-processing occurs
@@ -134,7 +137,6 @@ public void doFilter (HttpExchange exchange) throws IOException {
*/
public abstract void doFilter (HttpExchange exchange, Chain chain)
throws IOException;

/**
* Returns a short description of this {@code Filter}.
*
@@ -252,4 +254,64 @@ public String description() {
}
};
}

/**
* Returns a
* {@linkplain Filter#beforeHandler(String, Consumer) pre-processing Filter}
* that inspects and possibly adapts the request state.
*
* The {@code Request} returned by the {@link UnaryOperator requestOperator}
* will be the effective request state of the exchange. It is executed for
* each {@code HttpExchange} before invoking either the next filter in the
* chain or the exchange handler (if this is the final filter in the chain).
* Exceptions thrown by the {@code requestOperator} are not handled by the
* filter.
*
* @apiNote
* When the returned filter is invoked, it first invokes the
* {@code requestOperator} with the given exchange, {@code ex}, in order to
* retrieve the <i>adapted request state</i>. It then invokes the next
* filter in the chain or the exchange handler, passing an exchange
* equivalent to {@code ex} with the <i>adapted request state</i> set as the
* effective request state.
*
* <p> Example of adding the {@code "Foo"} request header to all requests:
* <pre>{@code
* var filter = Filter.adaptRequest("Add Foo header", r -> r.with("Foo", List.of("Bar")));
* httpContext.getFilters().add(filter);
* }</pre>
*
* @param description the string to be returned from {@link #description()}
* @param requestOperator the request operator
* @return a filter that adapts the request state before the exchange is handled
* @throws NullPointerException if any argument is null
* @since 18
*/
public static Filter adaptRequest(String description,
UnaryOperator<Request> requestOperator) {
Objects.requireNonNull(description);
Objects.requireNonNull(requestOperator);

return new Filter() {
@Override
public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
var request = requestOperator.apply(exchange);
var newExchange = new DelegatingHttpExchange(exchange) {
@Override
public URI getRequestURI() { return request.getRequestURI(); }

@Override
public String getRequestMethod() { return request.getRequestMethod(); }

@Override
public Headers getRequestHeaders() { return request.getRequestHeaders(); }
};
chain.doFilter(newExchange);
}
@Override
public String description() {
return description;
}
};
}
}
@@ -25,6 +25,7 @@

package com.sun.net.httpserver;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
@@ -33,6 +34,8 @@
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import sun.net.httpserver.UnmodifiableHeaders;

/**
* HTTP request and response headers are represented by this class which
@@ -65,6 +68,14 @@
* value given overwriting any existing values in the value list.
* </ul>
*
* <p> An instance of {@code Headers} is either <i>mutable</i> or <i>immutable</i>.
* A <i>mutable headers</i> allows to add, remove, or modify header names and
* values, e.g. the instance returned by {@link HttpExchange#getResponseHeaders()}.
* An <i>immutable headers</i> disallows any modification to header names or
* values, e.g. the instance returned by {@link HttpExchange#getRequestHeaders()}.
* The mutator methods for an immutable headers instance unconditionally throw
* {@code UnsupportedOperationException}.
*
* <p> All methods in this class reject {@code null} values for keys and values.
* {@code null} keys will never be present in HTTP request or response headers.
* @since 1.6
@@ -78,6 +89,25 @@ public class Headers implements Map<String,List<String>> {
*/
public Headers() {map = new HashMap<>(32);}

/**
* Creates a mutable {@code Headers} from the given {@code headers} with
* the same header names and values.
*
* @param headers a map of header names and values
* @throws NullPointerException if {@code headers} or any of its names or
* values are null, or if any value contains
* null.
* @since 18
*/
public Headers(Map<String,List<String>> headers) {
Objects.requireNonNull(headers);
var h = headers.entrySet().stream()
.collect(Collectors.toUnmodifiableMap(
Entry::getKey, e -> new LinkedList<>(e.getValue())));
map = new HashMap<>(32);
this.putAll(h);
}

/**
* Normalize the key by converting to following form.
* First {@code char} upper case, rest lower case.
@@ -254,4 +284,55 @@ public String toString() {
sb.append(" }");
return sb.toString();
}

/**
* Returns an immutable {@code Headers} with the given name value pairs as
* its set of headers.
*
* <p> The supplied {@code String} instances must alternate as header names
* and header values. To add several values to the same name, the same name
* must be supplied with each new value. If the supplied {@code headers} is
* empty, then an empty {@code Headers} is returned.
*
* @param headers the list of name value pairs
* @return an immutable headers with the given name value pairs
* @throws NullPointerException if {@code headers} or any of its
* elements are null.
* @throws IllegalArgumentException if the number of supplied strings is odd.
* @since 18
*/
public static Headers of(String... headers) {
Objects.requireNonNull(headers);
if (headers.length == 0) {
return new UnmodifiableHeaders(new Headers());
}
if (headers.length % 2 != 0) {
throw new IllegalArgumentException("wrong number, %d, of elements"
.formatted(headers.length));
}
Arrays.stream(headers).forEach(Objects::requireNonNull);

var h = new Headers();
for (int i = 0; i < headers.length; i += 2) {
String name = headers[i];
String value = headers[i + 1];
h.add(name, value);
}
return new UnmodifiableHeaders(h);
}

/**
* Returns an immutable {@code Headers} from the given {@code headers} with
* the same header names and values.
*
* @param headers a map of header names and values
* @return an immutable headers
* @throws NullPointerException if {@code headers} or any of its names or
* values are null, or if any value contains
* null.
* @since 18
*/
public static Headers of(Map<String,List<String>> headers) {
return new UnmodifiableHeaders(new Headers(headers));
}
}
@@ -69,7 +69,7 @@
* @since 1.6
*/

public abstract class HttpExchange implements AutoCloseable {
public abstract class HttpExchange implements AutoCloseable, Request {

/**
* Constructor for subclasses to call.
@@ -78,19 +78,8 @@ protected HttpExchange() {
}

/**
* Returns an immutable {@link Headers} containing the HTTP headers that
* were included with this request.
*
* <p> The keys in this {@code Headers} are the header names, while the
* values are a {@link java.util.List} of
* {@linkplain java.lang.String Strings} containing each value that was
* included in the request, in the order they were included. Header fields
* appearing multiple times are represented as multiple string values.
*
* <p> The keys in {@code Headers} are case-insensitive.
*
* @return a read-only {@code Headers} which can be used to access request
* headers.
* {@inheritDoc}
* @return {@inheritDoc}
*/
public abstract Headers getRequestHeaders();

@@ -111,16 +100,14 @@ protected HttpExchange() {
public abstract Headers getResponseHeaders();

/**
* Returns the request {@link URI}.
*
* @return the request {@code URI}
* {@inheritDoc}
* @return {@inheritDoc}
*/
public abstract URI getRequestURI();

/**
* Returns the request method.
*
* @return the request method
* {@inheritDoc}
* @return {@inheritDoc}
*/
public abstract String getRequestMethod();