package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.http; import com.google.protobuf.ByteString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.zip.GZIPOutputStream; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiHttpService; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.agps.AgpsHandler; public class HttpHandler { private static final Logger LOG = LoggerFactory.getLogger(HttpHandler.class); private final AgpsHandler agpsHandler; private final ContactsHandler contactsHandler; public HttpHandler(GarminSupport deviceSupport) { agpsHandler = new AgpsHandler(deviceSupport); contactsHandler = new ContactsHandler(deviceSupport); } public GdiHttpService.HttpService handle(final GdiHttpService.HttpService httpService) { if (httpService.hasRawRequest()) { final GdiHttpService.HttpService.RawResponse rawResponse = handleRawRequest(httpService.getRawRequest()); if (rawResponse != null) { return GdiHttpService.HttpService.newBuilder() .setRawResponse(rawResponse) .build(); } return null; } LOG.warn("Unsupported http service request {}", httpService); return null; } public GdiHttpService.HttpService.RawResponse handleRawRequest(final GdiHttpService.HttpService.RawRequest rawRequest) { LOG.debug("Got rawRequest: {} - {}", rawRequest.getMethod(), rawRequest.getUrl()); final GarminHttpRequest request = new GarminHttpRequest(rawRequest); final GarminHttpResponse response; if (request.getPath().startsWith("/weather/")) { LOG.info("Got weather request for {}", request.getPath()); response = WeatherHandler.handleWeatherRequest(request); } else if (request.getPath().startsWith("/ephemeris/")) { LOG.info("Got AGPS request for {}", request.getPath()); response = agpsHandler.handleAgpsRequest(request); } else if (request.getPath().startsWith("/device-gateway/usercontact/")) { LOG.info("Got contacts request for {}", request.getPath()); response = contactsHandler.handleRequest(request); } else { LOG.warn("Unhandled path {}", request.getPath()); response = null; } if (response == null) { return GdiHttpService.HttpService.RawResponse.newBuilder() .setStatus(GdiHttpService.HttpService.Status.UNKNOWN_STATUS) .build(); } LOG.debug("Http response status={}", response.getStatus()); return createRawResponse(request, response); } private static GdiHttpService.HttpService.RawResponse createRawResponse( final GarminHttpRequest request, final GarminHttpResponse response ) { final List responseHeaders = new ArrayList<>(); for (final Map.Entry h : response.getHeaders().entrySet()) { responseHeaders.add( GdiHttpService.HttpService.Header.newBuilder() .setKey(h.getKey()) .setValue(h.getValue()) .build() ); } if (response.getStatus() == 200 && request.getRawRequest().hasUseDataXfer() && request.getRawRequest().getUseDataXfer()) { LOG.debug("Data will be returned using data_xfer"); int id = DataTransferHandler.registerData(response.getBody()); if (response.getOnDataSuccessfullySentListener() != null) { DataTransferHandler.addOnDataSuccessfullySentListener(id, response.getOnDataSuccessfullySentListener()); } return GdiHttpService.HttpService.RawResponse.newBuilder() .setStatus(GdiHttpService.HttpService.Status.OK) .setHttpStatus(response.getStatus()) .addAllHeader(responseHeaders) .setXferData( GdiHttpService.HttpService.DataTransferItem.newBuilder() .setId(id) .setSize(response.getBody().length) .build() ) .build(); } final byte[] responseBody; if ("gzip".equals(request.getHeaders().get("accept-encoding"))) { LOG.debug("Compressing response"); responseHeaders.add( GdiHttpService.HttpService.Header.newBuilder() .setKey("Content-Encoding") .setValue("gzip") .build() ); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (GZIPOutputStream gzos = new GZIPOutputStream(baos)) { gzos.write(response.getBody()); gzos.finish(); gzos.flush(); responseBody = baos.toByteArray(); } catch (final Exception e) { LOG.error("Failed to compress response", e); return null; } } else { responseBody = response.getBody(); } return GdiHttpService.HttpService.RawResponse.newBuilder() .setStatus(response.getStatus() / 100 == 2 ? GdiHttpService.HttpService.Status.OK : GdiHttpService.HttpService.Status.UNKNOWN_STATUS) .setHttpStatus(response.getStatus()) .setBody(ByteString.copyFrom(responseBody)) .addAllHeader(responseHeaders) .build(); } }