001package ball.upnp.ssdp; 002/*- 003 * ########################################################################## 004 * UPnP/SSDP Implementation Classes 005 * $Id: SSDPResponse.java 7215 2021-01-03 18:39:51Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-upnp/trunk/src/main/java/ball/upnp/ssdp/SSDPResponse.java $ 007 * %% 008 * Copyright (C) 2013 - 2021 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.net.DatagramPacket; 024import java.net.InetSocketAddress; 025import java.net.SocketAddress; 026import java.net.URI; 027import java.util.List; 028import java.util.Objects; 029import java.util.function.Function; 030import java.util.regex.Pattern; 031import java.util.stream.Stream; 032import org.apache.http.Header; 033import org.apache.http.HttpVersion; 034import org.apache.http.message.BasicHttpResponse; 035 036import static java.nio.charset.StandardCharsets.UTF_8; 037import static java.util.stream.Collectors.joining; 038import static org.apache.commons.lang3.StringUtils.EMPTY; 039import static org.apache.http.message.BasicLineParser.INSTANCE; 040import static org.apache.http.message.BasicLineParser.parseHeader; 041import static org.apache.http.message.BasicLineParser.parseStatusLine; 042 043/** 044 * SSDP {@link org.apache.http.HttpResponse} implementation. 045 * 046 * {@bean.info} 047 * 048 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 049 * @version $Revision: 7215 $ 050 */ 051public class SSDPResponse extends BasicHttpResponse implements SSDPMessage { 052 053 /** 054 * Method to parse a {@link SSDPResponse} from a 055 * {@link DatagramPacket}. 056 * 057 * @param packet The {@link DatagramPacket}. 058 * 059 * @return A new {@link SSDPResponse}. 060 */ 061 public static SSDPResponse from(DatagramPacket packet) { 062 return new SSDPResponse(packet); 063 } 064 065 private SocketAddress address = null; 066 private long timestamp = System.currentTimeMillis(); 067 private Long expiration = null; 068 069 /** 070 * Sole non-private constructor. 071 * 072 * @param code The {@link SSDPRequest} {@code code}. 073 * @param reason The {@link SSDPRequest} reason. 074 */ 075 protected SSDPResponse(int code, String reason) { 076 super(HttpVersion.HTTP_1_1, code, reason); 077 } 078 079 private SSDPResponse(DatagramPacket packet) { 080 this(packet, SSDPMessage.parse(packet)); 081 } 082 083 private SSDPResponse(DatagramPacket packet, List<String> lines) { 084 super(parseStatusLine(lines.remove(0), INSTANCE)); 085 086 lines.stream().forEach(t -> addHeader(parseHeader(t, INSTANCE))); 087 088 address = packet.getSocketAddress(); 089 } 090 091 /** 092 * Method to get the {@link SocketAddress} from the 093 * {@link DatagramPacket} if {@link.this} {@link SSDPResponse} was 094 * parsed from a packet. 095 * 096 * @return The {@link SocketAddress}. 097 */ 098 public SocketAddress getSocketAddress() { return address; } 099 100 /** 101 * {@link String} fluent header setter. 102 * 103 * @param name The header name. 104 * @param value The header value. 105 * 106 * @return {@link.this} 107 */ 108 public SSDPResponse header(String name, String value) { 109 setHeader(name, value); 110 111 return this; 112 } 113 114 /** 115 * {@link SocketAddress} fluent header setter. 116 * 117 * @param name The header name. 118 * @param value The header value. 119 * 120 * @return {@link.this} 121 */ 122 public SSDPResponse header(String name, SocketAddress value) { 123 return header(name, (InetSocketAddress) value); 124 } 125 126 /** 127 * {@link InetSocketAddress} fluent header setter. 128 * 129 * @param name The header name. 130 * @param value The header value. 131 * 132 * @return {@link.this} 133 */ 134 public SSDPResponse header(String name, InetSocketAddress value) { 135 return header(name, 136 t -> String.format("%s:%d", 137 value.getAddress().getHostAddress(), 138 value.getPort()), 139 value); 140 } 141 142 /** 143 * {@link Number} fluent header setter. 144 * 145 * @param name The header name. 146 * @param value The header value. 147 * 148 * @return {@link.this} 149 */ 150 public SSDPResponse header(String name, Number value) { 151 return header(name, Number::toString, value); 152 } 153 154 /** 155 * {@link URI} fluent header setter. 156 * 157 * @param name The header name. 158 * @param value The header value. 159 * 160 * @return {@link.this} 161 */ 162 public SSDPResponse header(String name, URI value) { 163 return header(name, URI::toASCIIString, value); 164 } 165 166 /** 167 * Fluent header setter. 168 * 169 * @param <T> The target type. 170 * @param name The header name. 171 * @param value The header value. 172 * 173 * @return {@link.this} 174 */ 175 public <T> SSDPResponse header(String name, 176 Function<T,String> function, T value) { 177 setHeader(name, (value != null) ? function.apply(value) : null); 178 179 return this; 180 } 181 182 @Override 183 public long getExpiration() { 184 if (expiration == null) { 185 expiration = SSDPMessage.getExpiration(this, timestamp); 186 } 187 188 return expiration; 189 } 190 191 @Override 192 public String toString() { 193 String string = 194 Stream.concat(Stream.of(getStatusLine()), 195 Stream.of(getAllHeaders())) 196 .filter(Objects::nonNull) 197 .map(Objects::toString) 198 .collect(joining(EOL, EMPTY, EOM)); 199 200 return string; 201 } 202}