Skip to content

Commit

Permalink
Merge pull request #12441 from jetty/jetty-12.0.x-12429-caseInsensiti…
Browse files Browse the repository at this point in the history
…veHeadersWebSocket

Issue #12429 - case-insensitive headers for websocket
  • Loading branch information
lachlan-roberts authored Nov 12, 2024
2 parents f78cbc1 + 7eb54fa commit b57b8e6
Show file tree
Hide file tree
Showing 34 changed files with 856 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,21 @@ default int size()
return size;
}

/**
* <p>Wraps an instance of {@link HttpFields} as a {@link Map}.</p>
* <p>If the provided {@link HttpFields} is an instance of {@link HttpFields.Mutable} then changes to the
* {@link Map} will be reflected in the underlying {@link HttpFields}.
* Otherwise, any modification to the {@link Map} will throw {@link UnsupportedOperationException}.</p>
* @param fields the {@link HttpFields} to convert to a {@link Map}.
* @return an {@link Map} representing the contents of the {@link HttpFields}.
*/
static Map<String, List<String>> asMap(HttpFields fields)
{
return (fields instanceof HttpFields.Mutable mutable)
? new HttpFieldsMap.Mutable(mutable)
: new HttpFieldsMap.Immutable(fields);
}

/**
* @return a sequential stream of the {@link HttpField}s in this instance
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.eclipse.jetty.util.StringUtil;

/**
* <p>A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.</p>
* @see HttpFieldsMap.Mutable
* @see HttpFieldsMap.Immutable
*/
abstract class HttpFieldsMap extends AbstractMap<String, List<String>>
{
/**
* <p>A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.</p>
* <p>Any changes to the {@link java.util.Map} will be reflected in the underlying instance of {@link HttpFields.Mutable}.</p>
*/
public static class Mutable extends HttpFieldsMap
{
private final HttpFields.Mutable httpFields;

public Mutable(HttpFields.Mutable httpFields)
{
this.httpFields = httpFields;
}

@Override
public List<String> get(Object key)
{
if (key instanceof String s)
return httpFields.getValuesList(s);
return null;
}

@Override
public List<String> put(String key, List<String> value)
{
List<String> oldValue = get(key);
httpFields.put(key, value);
return oldValue;
}

@Override
public List<String> remove(Object key)
{
if (key instanceof String s)
{
List<String> oldValue = get(s);
httpFields.remove(s);
return oldValue;
}
return null;
}

@Override
public Set<Entry<String, List<String>>> entrySet()
{
return new AbstractSet<>()
{
@Override
public Iterator<Entry<String, List<String>>> iterator()
{
return new Iterator<>()
{
private final Iterator<String> iterator = httpFields.getFieldNamesCollection().iterator();
private String name = null;

@Override
public boolean hasNext()
{
return iterator.hasNext();
}

@Override
public Entry<String, List<String>> next()
{
name = iterator.next();
return new HttpFieldsEntry(name);
}

@Override
public void remove()
{
if (name != null)
{
Mutable.this.remove(name);
name = null;
}
}
};
}

@Override
public int size()
{
return httpFields.getFieldNamesCollection().size();
}
};
}
}

/**
* <p>A {@link java.util.Map} which is backed by an instance of {@link HttpFields.Mutable}.</p>
* <p>Any attempt to modify the map will throw {@link UnsupportedOperationException}.</p>
*/
public static class Immutable extends HttpFieldsMap
{
private final HttpFields httpFields;

public Immutable(HttpFields httpFields)
{
this.httpFields = httpFields;
}

@Override
public List<String> get(Object key)
{
if (key instanceof String s)
return httpFields.getValuesList(s);
return null;
}

@Override
public List<String> put(String key, List<String> value)
{
throw new UnsupportedOperationException();
}

@Override
public List<String> remove(Object key)
{
throw new UnsupportedOperationException();
}

@Override
public Set<Entry<String, List<String>>> entrySet()
{
return new AbstractSet<>()
{
@Override
public Iterator<Entry<String, List<String>>> iterator()
{
return new Iterator<>()
{
private final Iterator<String> iterator = httpFields.getFieldNamesCollection().iterator();

@Override
public boolean hasNext()
{
return iterator.hasNext();
}

@Override
public Entry<String, List<String>> next()
{
return new HttpFieldsEntry(iterator.next());
}

@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}

@Override
public int size()
{
return httpFields.getFieldNamesCollection().size();
}
};
}
}

private class HttpFieldsEntry implements Entry<String, List<String>>
{
private final String _name;

public HttpFieldsEntry(String name)
{
_name = name;
}

@Override
public String getKey()
{
return _name;
}

@Override
public List<String> getValue()
{
return HttpFieldsMap.this.get(_name);
}

@Override
public List<String> setValue(List<String> value)
{
return HttpFieldsMap.this.put(_name, value);
}

@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o instanceof HttpFieldsEntry other)
return StringUtil.asciiEqualsIgnoreCase(_name, other.getKey());
return false;
}

@Override
public int hashCode()
{
return Objects.hash(StringUtil.asciiToLowerCase(_name));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.stream.Collectors;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.EndPoint;
Expand Down Expand Up @@ -78,7 +79,7 @@ public List<String> getHeaders(String name)
@Override
public Map<String, List<String>> getHeaders()
{
return null;
return HttpFields.asMap(delegate.getHeaders());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@

package org.eclipse.jetty.websocket.client.internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
Expand Down Expand Up @@ -65,9 +65,7 @@ public List<String> getHeaders(String name)
@Override
public Map<String, List<String>> getHeaders()
{
Map<String, List<String>> headers = getHeaderNames().stream()
.collect(Collectors.toMap((name) -> name, (name) -> new ArrayList<>(getHeaders(name))));
return Collections.unmodifiableMap(headers);
return HttpFields.asMap(delegate.getHeaders());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,12 @@ private org.eclipse.jetty.websocket.core.server.WebSocketCreator newWebSocketCre
{
try
{
Object webSocket = creator.createWebSocket(new ServerUpgradeRequestDelegate(rq), new ServerUpgradeResponseDelegate(rq, rs), cb);
if (webSocket == null)
cb.succeeded();
return webSocket;
return creator.createWebSocket(new ServerUpgradeRequestDelegate(rq), new ServerUpgradeResponseDelegate(rq, rs), cb);
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("Could not create WebSocket endpoint", x);
cb.failed(x);
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Map;
import java.util.stream.Collectors;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
Expand Down Expand Up @@ -73,14 +72,7 @@ public int getHeaderInt(String name)
@Override
public Map<String, List<String>> getHeaders()
{
Map<String, List<String>> result = new LinkedHashMap<>();
HttpFields headers = request.getHeaders();
for (HttpField header : headers)
{
String name = header.getName();
result.put(name, headers.getValuesList(name));
}
return result;
return HttpFields.asMap(request.getHeaders());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@

package org.eclipse.jetty.websocket.server.internal;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.websocket.api.ExtensionConfig;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
Expand Down Expand Up @@ -64,14 +62,7 @@ public Set<String> getHeaderNames()
@Override
public Map<String, List<String>> getHeaders()
{
Map<String, List<String>> result = new LinkedHashMap<>();
HttpFields.Mutable headers = response.getHeaders();
for (HttpField header : headers)
{
String name = header.getName();
result.put(name, headers.getValuesList(name));
}
return result;
return HttpFields.asMap(response.getHeaders());
}

@Override
Expand Down
Loading

0 comments on commit b57b8e6

Please # to comment.