1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.lfs.server.s3;
45
46 import static javax.servlet.http.HttpServletResponse.SC_OK;
47 import static org.eclipse.jgit.lfs.server.s3.SignerV4.UNSIGNED_PAYLOAD;
48 import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_CONTENT_SHA256;
49 import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_EXPIRES;
50 import static org.eclipse.jgit.lfs.server.s3.SignerV4.X_AMZ_STORAGE_CLASS;
51 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
52 import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
53 import static org.eclipse.jgit.util.HttpSupport.METHOD_HEAD;
54 import static org.eclipse.jgit.util.HttpSupport.METHOD_PUT;
55
56 import java.io.IOException;
57 import java.net.MalformedURLException;
58 import java.net.Proxy;
59 import java.net.ProxySelector;
60 import java.net.URL;
61 import java.text.MessageFormat;
62 import java.util.HashMap;
63 import java.util.Map;
64
65 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
66 import org.eclipse.jgit.lfs.server.LargeFileRepository;
67 import org.eclipse.jgit.lfs.server.Response;
68 import org.eclipse.jgit.lfs.server.Response.Action;
69 import org.eclipse.jgit.lfs.server.internal.LfsServerText;
70 import org.eclipse.jgit.transport.http.HttpConnection;
71 import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
72 import org.eclipse.jgit.util.HttpSupport;
73
74
75
76
77
78
79 public class S3Repository implements LargeFileRepository {
80
81 private S3Config s3Config;
82
83
84
85
86
87
88
89 public S3Repository(S3Config config) {
90 validateConfig(config);
91 this.s3Config = config;
92 }
93
94 @Override
95 public Response.Action getDownloadAction(AnyLongObjectId oid) {
96 URL endpointUrl = getObjectUrl(oid);
97 Map<String, String> queryParams = new HashMap<String, String>();
98 queryParams.put(X_AMZ_EXPIRES,
99 Integer.toString(s3Config.getExpirationSeconds()));
100 Map<String, String> headers = new HashMap<String, String>();
101 String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
102 s3Config, endpointUrl, METHOD_GET, headers, queryParams,
103 UNSIGNED_PAYLOAD);
104
105 Response.Action a = new Response.Action();
106 a.href = endpointUrl.toString() + "?" + authorizationQueryParameters;
107 return a;
108 }
109
110 @Override
111 public Response.Action getUploadAction(AnyLongObjectId oid, long size) {
112 cacheObjectMetaData(oid, size);
113 URL objectUrl = getObjectUrl(oid);
114 Map<String, String> headers = new HashMap<String, String>();
115 headers.put(X_AMZ_CONTENT_SHA256, oid.getName());
116 headers.put(HDR_CONTENT_LENGTH, Long.toString(size));
117 headers.put(X_AMZ_STORAGE_CLASS, s3Config.getStorageClass());
118 headers.put(HttpSupport.HDR_CONTENT_TYPE, "application/octet-stream");
119 headers = SignerV4.createHeaderAuthorization(s3Config, objectUrl,
120 METHOD_PUT, headers, oid.getName());
121
122 Response.Action a = new Response.Action();
123 a.href = objectUrl.toString();
124 a.header = new HashMap<>();
125 a.header.putAll(headers);
126 return a;
127 }
128
129 @Override
130 public Action getVerifyAction(AnyLongObjectId id) {
131 return null;
132 }
133
134 @Override
135 public long getSize(AnyLongObjectId oid) throws IOException {
136 URL endpointUrl = getObjectUrl(oid);
137 Map<String, String> queryParams = new HashMap<String, String>();
138 queryParams.put(X_AMZ_EXPIRES,
139 Integer.toString(s3Config.getExpirationSeconds()));
140 Map<String, String> headers = new HashMap<String, String>();
141
142 String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
143 s3Config, endpointUrl, METHOD_HEAD, headers, queryParams,
144 UNSIGNED_PAYLOAD);
145 String href = endpointUrl.toString() + "?"
146 + authorizationQueryParameters;
147
148 Proxy proxy = HttpSupport.proxyFor(ProxySelector.getDefault(),
149 endpointUrl);
150 HttpClientConnectionFactory f = new HttpClientConnectionFactory();
151 HttpConnection conn = f.create(new URL(href), proxy);
152 if (s3Config.isDisableSslVerify()) {
153 HttpSupport.disableSslVerify(conn);
154 }
155 conn.setRequestMethod(METHOD_HEAD);
156 conn.connect();
157 int status = conn.getResponseCode();
158 if (status == SC_OK) {
159 String contentLengthHeader = conn
160 .getHeaderField(HDR_CONTENT_LENGTH);
161 if (contentLengthHeader != null) {
162 return Integer.parseInt(contentLengthHeader);
163 }
164 }
165 return -1;
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179
180 protected void cacheObjectMetaData(AnyLongObjectId oid, long size) {
181
182 }
183
184 private void validateConfig(S3Config config) {
185 assertNotEmpty(LfsServerText.get().undefinedS3AccessKey,
186 config.getAccessKey());
187 assertNotEmpty(LfsServerText.get().undefinedS3Bucket,
188 config.getBucket());
189 assertNotEmpty(LfsServerText.get().undefinedS3Region,
190 config.getRegion());
191 assertNotEmpty(LfsServerText.get().undefinedS3SecretKey,
192 config.getSecretKey());
193 assertNotEmpty(LfsServerText.get().undefinedS3StorageClass,
194 config.getStorageClass());
195 }
196
197 private void assertNotEmpty(String message, String value) {
198 if (value == null || value.trim().length() == 0) {
199 throw new IllegalArgumentException(message);
200 }
201 }
202
203 private URL getObjectUrl(AnyLongObjectId oid) {
204 try {
205 return new URL(String.format("https://s3-%s.amazonaws.com/%s/%s", //$NON-NLS-1$
206 s3Config.getRegion(), s3Config.getBucket(),
207 getPath(oid)));
208 } catch (MalformedURLException e) {
209 throw new IllegalArgumentException(MessageFormat.format(
210 LfsServerText.get().unparsableEndpoint, e.getMessage()));
211 }
212 }
213
214 private String getPath(AnyLongObjectId oid) {
215 return oid.getName();
216 }
217 }