View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.transport.impl;
17  
18  import java.util.logging.Logger;
19  import java.util.logging.Level;
20  
21  import org.fourthline.cling.model.message.IncomingDatagramMessage;
22  import org.fourthline.cling.model.message.OutgoingDatagramMessage;
23  import org.fourthline.cling.model.message.UpnpHeaders;
24  import org.fourthline.cling.model.message.UpnpOperation;
25  import org.fourthline.cling.model.message.UpnpRequest;
26  import org.fourthline.cling.model.message.UpnpResponse;
27  import org.fourthline.cling.transport.spi.DatagramProcessor;
28  import org.fourthline.cling.model.UnsupportedDataException;
29  import org.seamless.http.Headers;
30  
31  import java.io.ByteArrayInputStream;
32  import java.io.UnsupportedEncodingException;
33  import java.net.DatagramPacket;
34  import java.net.InetAddress;
35  import java.util.Locale;
36  
37  /**
38   * Default implementation.
39   * 
40   * @author Christian Bauer
41   */
42  public class DatagramProcessorImpl implements DatagramProcessor {
43  
44      private static Logger log = Logger.getLogger(DatagramProcessor.class.getName());
45  
46      public IncomingDatagramMessage read(InetAddress receivedOnAddress, DatagramPacket datagram) throws UnsupportedDataException {
47  
48          try {
49  
50              if (log.isLoggable(Level.FINER)) {
51                  log.finer("===================================== DATAGRAM BEGIN ============================================");
52                  log.finer(new String(datagram.getData(), "UTF-8"));
53                  log.finer("-===================================== DATAGRAM END =============================================");
54              }
55  
56              ByteArrayInputStream is = new ByteArrayInputStream(datagram.getData());
57  
58              String[] startLine = Headers.readLine(is).split(" ");
59              if (startLine[0].startsWith("HTTP/1.")) {
60                  return readResponseMessage(receivedOnAddress, datagram, is, Integer.valueOf(startLine[1]), startLine[2], startLine[0]);
61              } else {
62                  return readRequestMessage(receivedOnAddress, datagram, is, startLine[0], startLine[2]);
63              }
64  
65          } catch (Exception ex) {
66              throw new UnsupportedDataException("Could not parse headers: " + ex, ex, datagram.getData());
67          }
68      }
69  
70      public DatagramPacket write(OutgoingDatagramMessage message) throws UnsupportedDataException {
71  
72          StringBuilder statusLine = new StringBuilder();
73  
74          UpnpOperation operation = message.getOperation();
75  
76          if (operation instanceof UpnpRequest) {
77  
78              UpnpRequest requestOperation = (UpnpRequest) operation;
79              statusLine.append(requestOperation.getHttpMethodName()).append(" * ");
80              statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append("\r\n");
81  
82          } else if (operation instanceof UpnpResponse) {
83              UpnpResponse responseOperation = (UpnpResponse) operation;
84              statusLine.append("HTTP/1.").append(operation.getHttpMinorVersion()).append(" ");
85              statusLine.append(responseOperation.getStatusCode()).append(" ").append(responseOperation.getStatusMessage());
86              statusLine.append("\r\n");
87          } else {
88              throw new UnsupportedDataException(
89                      "Message operation is not request or response, don't know how to process: " + message
90              );
91          }
92  
93          // UDA 1.0, 1.1.2: No body but message must have a blank line after header
94          StringBuilder messageData = new StringBuilder();
95          messageData.append(statusLine);
96  
97          messageData.append(message.getHeaders().toString()).append("\r\n");
98  
99          if (log.isLoggable(Level.FINER)) {
100             log.finer("Writing message data for: " + message);
101             log.finer("---------------------------------------------------------------------------------");
102             log.finer(messageData.toString().substring(0, messageData.length() - 2)); // Don't print the blank lines
103             log.finer("---------------------------------------------------------------------------------");
104         }
105 
106         try {
107             // According to HTTP 1.0 RFC, headers and their values are US-ASCII
108             // TODO: Probably should look into escaping rules, too
109             byte[] data = messageData.toString().getBytes("US-ASCII");
110 
111             log.fine("Writing new datagram packet with " + data.length + " bytes for: " + message);
112             return new DatagramPacket(data, data.length, message.getDestinationAddress(), message.getDestinationPort());
113 
114         } catch (UnsupportedEncodingException ex) {
115             throw new UnsupportedDataException(
116                 "Can't convert message content to US-ASCII: " + ex.getMessage(), ex, messageData
117             );
118         }
119     }
120 
121     protected IncomingDatagramMessage readRequestMessage(InetAddress receivedOnAddress,
122                                                          DatagramPacket datagram,
123                                                          ByteArrayInputStream is,
124                                                          String requestMethod,
125                                                          String httpProtocol) throws Exception {
126 
127         // Headers
128         UpnpHeaders headers = new UpnpHeaders(is);
129 
130         // Assemble message
131         IncomingDatagramMessage requestMessage;
132         UpnpRequest upnpRequest = new UpnpRequest(UpnpRequest.Method.getByHttpName(requestMethod));
133         upnpRequest.setHttpMinorVersion(httpProtocol.toUpperCase(Locale.ROOT).equals("HTTP/1.1") ? 1 : 0);
134         requestMessage = new IncomingDatagramMessage(upnpRequest, datagram.getAddress(), datagram.getPort(), receivedOnAddress);
135 
136         requestMessage.setHeaders(headers);
137 
138         return requestMessage;
139     }
140 
141     protected IncomingDatagramMessage readResponseMessage(InetAddress receivedOnAddress,
142                                                           DatagramPacket datagram,
143                                                           ByteArrayInputStream is,
144                                                           int statusCode,
145                                                           String statusMessage,
146                                                           String httpProtocol) throws Exception {
147 
148         // Headers
149         UpnpHeaders headers = new UpnpHeaders(is);
150 
151         // Assemble the message
152         IncomingDatagramMessage responseMessage;
153         UpnpResponse upnpResponse = new UpnpResponse(statusCode, statusMessage);
154         upnpResponse.setHttpMinorVersion(httpProtocol.toUpperCase(Locale.ROOT).equals("HTTP/1.1") ? 1 : 0);
155         responseMessage = new IncomingDatagramMessage(upnpResponse, datagram.getAddress(), datagram.getPort(), receivedOnAddress);
156 
157         responseMessage.setHeaders(headers);
158 
159         return responseMessage;
160     }
161 
162 
163 }