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
95 @Override
96 public Response.Action getDownloadAction(AnyLongObjectId oid) {
97 URL endpointUrl = getObjectUrl(oid);
98 Map<String, String> queryParams = new HashMap<>();
99 queryParams.put(X_AMZ_EXPIRES,
100 Integer.toString(s3Config.getExpirationSeconds()));
101 Map<String, String> headers = new HashMap<>();
102 String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
103 s3Config, endpointUrl, METHOD_GET, headers, queryParams,
104 UNSIGNED_PAYLOAD);
105
106 Response.Action a = new Response.Action();
107 a.href = endpointUrl.toString() + "?" + authorizationQueryParameters;
108 return a;
109 }
110
111
112 @Override
113 public Response.Action getUploadAction(AnyLongObjectId oid, long size) {
114 cacheObjectMetaData(oid, size);
115 URL objectUrl = getObjectUrl(oid);
116 Map<String, String> headers = new HashMap<>();
117 headers.put(X_AMZ_CONTENT_SHA256, oid.getName());
118 headers.put(HDR_CONTENT_LENGTH, Long.toString(size));
119 headers.put(X_AMZ_STORAGE_CLASS, s3Config.getStorageClass());
120 headers.put(HttpSupport.HDR_CONTENT_TYPE, "application/octet-stream");
121 headers = SignerV4.createHeaderAuthorization(s3Config, objectUrl,
122 METHOD_PUT, headers, oid.getName());
123
124 Response.Action a = new Response.Action();
125 a.href = objectUrl.toString();
126 a.header = new HashMap<>();
127 a.header.putAll(headers);
128 return a;
129 }
130
131
132 @Override
133 public Action getVerifyAction(AnyLongObjectId id) {
134 return null;
135 }
136
137
138 @Override
139 public long getSize(AnyLongObjectId oid) throws IOException {
140 URL endpointUrl = getObjectUrl(oid);
141 Map<String, String> queryParams = new HashMap<>();
142 queryParams.put(X_AMZ_EXPIRES,
143 Integer.toString(s3Config.getExpirationSeconds()));
144 Map<String, String> headers = new HashMap<>();
145
146 String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
147 s3Config, endpointUrl, METHOD_HEAD, headers, queryParams,
148 UNSIGNED_PAYLOAD);
149 String href = endpointUrl.toString() + "?"
150 + authorizationQueryParameters;
151
152 Proxy proxy = HttpSupport.proxyFor(ProxySelector.getDefault(),
153 endpointUrl);
154 HttpClientConnectionFactory f = new HttpClientConnectionFactory();
155 HttpConnection conn = f.create(new URL(href), proxy);
156 if (s3Config.isDisableSslVerify()) {
157 HttpSupport.disableSslVerify(conn);
158 }
159 conn.setRequestMethod(METHOD_HEAD);
160 conn.connect();
161 int status = conn.getResponseCode();
162 if (status == SC_OK) {
163 String contentLengthHeader = conn
164 .getHeaderField(HDR_CONTENT_LENGTH);
165 if (contentLengthHeader != null) {
166 return Integer.parseInt(contentLengthHeader);
167 }
168 }
169 return -1;
170 }
171
172
173
174
175
176
177
178
179
180
181
182
183
184 protected void cacheObjectMetaData(AnyLongObjectId oid, long size) {
185
186 }
187
188 private void validateConfig(S3Config config) {
189 assertNotEmpty(LfsServerText.get().undefinedS3AccessKey,
190 config.getAccessKey());
191 assertNotEmpty(LfsServerText.get().undefinedS3Bucket,
192 config.getBucket());
193 assertNotEmpty(LfsServerText.get().undefinedS3Region,
194 config.getRegion());
195 assertNotEmpty(LfsServerText.get().undefinedS3SecretKey,
196 config.getSecretKey());
197 assertNotEmpty(LfsServerText.get().undefinedS3StorageClass,
198 config.getStorageClass());
199 }
200
201 private void assertNotEmpty(String message, String value) {
202 if (value == null || value.trim().length() == 0) {
203 throw new IllegalArgumentException(message);
204 }
205 }
206
207 private URL getObjectUrl(AnyLongObjectId oid) {
208 try {
209 return new URL(String.format("https://s3-%s.amazonaws.com/%s/%s", //$NON-NLS-1$
210 s3Config.getRegion(), s3Config.getBucket(),
211 getPath(oid)));
212 } catch (MalformedURLException e) {
213 throw new IllegalArgumentException(MessageFormat.format(
214 LfsServerText.get().unparsableEndpoint, e.getMessage()));
215 }
216 }
217
218 private String getPath(AnyLongObjectId oid) {
219 return oid.getName();
220 }
221 }